13、指纹与Face ID统一处理:BiometricManager统一管理、自动选择最佳生物识别方式、用户偏好设置
说实话,做Android生物识别开发最头疼的是什么?
不是指纹识别有多难,也不是人脸识别有多复杂。而是——设备太多了!有的手机只有指纹,有的只有人脸,有的两个都有。你总不能给每个设备写一套逻辑吧?
嗯,Google也想到了这个问题。所以推出了BiometricManager,帮我们把指纹、人脸、虹膜这些乱七八糟的生物识别方式统一管理起来。今天我们就聊聊怎么用好它。
13.1 为什么需要统一处理?
我早期做项目时,遇到过这么个情况:
一个App同时支持指纹和Face ID。当时我分别调用了FingerprintManager和FaceManager,各写一套回调逻辑。结果测试的时候发现——有些手机只有指纹,调用FaceManager直接崩溃;有些手机两个都有,用户想用哪个还得手动切。
说白了,这就是典型的「各管各的」带来的问题。
BiometricManager的出现,就是为了解决这个痛点。它把所有的生物识别方式抽象成统一的API,你只需要关心「用户是否通过了生物识别验证」,至于用的是指纹还是人脸,系统帮你搞定。
13.2 BiometricManager的核心类
我们先看看几个关键类:
| 类名 | 作用 |
|---|---|
| BiometricManager | 检测设备是否支持生物识别,以及当前是否可用 |
| BiometricPrompt | 弹出系统级生物识别对话框,处理验证流程 |
| BiometricPrompt.AuthenticationCallback | 验证结果回调(成功、失败、错误等) |
| BiometricPrompt.PromptInfo | 配置对话框的标题、描述、取消按钮等 |
我个人习惯是:先用BiometricManager做一次「体检」,看看设备能不能用生物识别。如果不行,再降级到密码或PIN码。如果行,就用BiometricPrompt弹出系统对话框。
13.3 自动选择最佳生物识别方式
你想想看,用户可能既录了指纹,又录了人脸。那App该用哪个?
其实不用你操心。BiometricManager内部有一套优先级逻辑:
- 强生物识别优先:比如3D人脸、超声波指纹这类安全性高的
- 弱生物识别次之:比如2D人脸、普通光学指纹
- 设备默认方式:如果系统有默认偏好,就按系统来
我记得有一次,用户反馈说「我明明录了指纹,为什么每次都是人脸识别?」
查了一下才发现,那台手机的人脸识别是3D结构光,指纹是光学指纹。系统自动选择了安全性更高的3D人脸。嗯,这其实是合理的。
PromptInfo中设置setAllowedAuthenticators()来限定。
13.4 用户偏好设置
虽然系统会自动选择最佳方式,但有些用户就是有「执念」——我就想用指纹,不想用Face ID。怎么办?
我的做法是:在App设置页面加一个「生物识别方式选择」的开关。
具体实现思路:
- 用
BiometricManager检测设备支持哪些方式 - 把支持的方式列出来,让用户勾选
- 把用户的选择存到SharedPreferences里
- 调用
BiometricPrompt时,根据用户偏好设置setAllowedAuthenticators()
代码大概长这样:
// 检测设备支持的生物识别方式
BiometricManager manager = BiometricManager.from(context);
int canAuthenticate = manager.canAuthenticate();
if (canAuthenticate == BiometricManager.BIOMETRIC_SUCCESS) {
// 设备支持生物识别
List<Integer> authenticators = new ArrayList<>();
// 检查是否支持指纹
if (manager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)
== BiometricManager.BIOMETRIC_SUCCESS) {
authenticators.add(BiometricManager.Authenticators.BIOMETRIC_WEAK);
}
// 检查是否支持人脸
if (manager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG)
== BiometricManager.BIOMETRIC_SUCCESS) {
authenticators.add(BiometricManager.Authenticators.BIOMETRIC_STRONG);
}
// 根据用户偏好过滤
String userPreference = preferences.getString("biometric_type", "auto");
if ("fingerprint".equals(userPreference)) {
authenticators.remove(BiometricManager.Authenticators.BIOMETRIC_STRONG);
} else if ("face".equals(userPreference)) {
authenticators.remove(BiometricManager.Authenticators.BIOMETRIC_WEAK);
}
// 构建PromptInfo
PromptInfo promptInfo = new PromptInfo.Builder()
.setTitle("验证身份")
.setSubtitle("请使用生物识别")
.setAllowedAuthenticators(authenticators)
.build();
}
setAllowedAuthenticators()会抛异常。所以一定要先做检测,再设置。
13.5 知识体系总览
下面这张图,是我自己总结的BiometricManager统一处理流程:
13.6 避坑指南
最后,分享几个我踩过的坑:
- 不要直接调用FingerprintManager:从Android 9开始,Google就建议用BiometricPrompt替代旧的指纹API。我见过不少老项目还在用FingerprintManager,结果在Android 12上直接报错。
- 注意BIOMETRIC_ERROR_NONE_ENROLLED:用户虽然设备支持生物识别,但没录入任何指纹或人脸。这时候应该提示用户去设置里录入,而不是直接报错。
- cancellationSignal要妥善管理:每次调用BiometricPrompt时,都要创建一个新的CancellationSignal。如果复用旧的,会导致无法取消上一次的验证。
- 不要在onResume里直接弹对话框:Activity还没完全显示时弹对话框,容易引发WindowManager.BadTokenException。我习惯用Handler.postDelayed延迟100ms再弹。
androidx.biometric库。它帮你处理了所有版本差异,省心很多。
好了,关于BiometricManager的统一处理就聊这么多。说白了就是一句话:让系统帮你做选择,让用户自己做决定。你只需要做好检测、配置和回调处理,剩下的交给BiometricManager就好。
公众号:蓝海资料掘金营,微信deep3321