13、指纹与Face ID统一处理:BiometricManager统一管理、自动选择最佳生物识别方式、用户偏好设置

说实话,做Android生物识别开发最头疼的是什么?

不是指纹识别有多难,也不是人脸识别有多复杂。而是——设备太多了!有的手机只有指纹,有的只有人脸,有的两个都有。你总不能给每个设备写一套逻辑吧?

嗯,Google也想到了这个问题。所以推出了BiometricManager,帮我们把指纹、人脸、虹膜这些乱七八糟的生物识别方式统一管理起来。今天我们就聊聊怎么用好它。

13.1 为什么需要统一处理?

我早期做项目时,遇到过这么个情况:

一个App同时支持指纹和Face ID。当时我分别调用了FingerprintManagerFaceManager,各写一套回调逻辑。结果测试的时候发现——有些手机只有指纹,调用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设置页面加一个「生物识别方式选择」的开关。

具体实现思路:

  1. BiometricManager检测设备支持哪些方式
  2. 把支持的方式列出来,让用户勾选
  3. 把用户的选择存到SharedPreferences里
  4. 调用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统一处理流程:

BiometricManager 统一处理流程 1. 检测设备能力 2. 是否支持生物识别? 降级到密码/PIN 3. 读取用户偏好设置 4. 构建PromptInfo 5. 弹出系统对话框

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再弹。
我的建议: 如果你要兼容Android 6.0到Android 13+,直接用androidx.biometric库。它帮你处理了所有版本差异,省心很多。

好了,关于BiometricManager的统一处理就聊这么多。说白了就是一句话:让系统帮你做选择,让用户自己做决定。你只需要做好检测、配置和回调处理,剩下的交给BiometricManager就好。


公众号:蓝海资料掘金营,微信deep3321