6、认证回调详解:onAuthenticationError、onAuthenticationSucceeded、onAuthenticationFailed、onAuthenticationResult
好,咱们接着聊。上一节我们把 BiometricPrompt 的实例给 new 出来了,也把对话框给弹起来了。但说实话,光会弹窗没用,你得知道用户到底有没有通过验证,对吧?
这就涉及到我们今天要讲的核心——认证回调。说白了,就是系统在认证完成后,会通过几个固定的方法告诉你结果。你只要把这些方法重写掉,在里面写你自己的业务逻辑就行了。
我个人习惯把回调分成两类:成功类和失败/异常类。咱们一个一个来看。
6.1 回调接口总览
Android 官方提供了一个叫 BiometricPrompt.AuthenticationCallback 的抽象类。你创建 BiometricPrompt 实例的时候,必须传一个它的子类进去。这个类里有四个核心方法需要你关注:
| 方法名 | 触发时机 | 常见原因 |
|---|---|---|
| onAuthenticationError | 发生不可恢复的错误时 | 传感器硬件故障、用户取消次数过多、系统锁定 |
| onAuthenticationSucceeded | 认证成功时 | 虹膜/指纹匹配成功 |
| onAuthenticationFailed | 认证失败时(可重试) | 虹膜/指纹不匹配 |
| onAuthenticationResult | 认证结果确定时 | 成功或失败后的最终回调(API 30+) |
嗯,这里要注意一下。很多新手会把 onAuthenticationFailed 和 onAuthenticationError 搞混。我简单解释一下:
- Failed 是「这次没对上,但你可以再试一次」。
- Error 是「系统已经罢工了,你别再试了」。
你想想看,如果用户虹膜没对准镜头,系统会回调 Failed,然后对话框还在,用户可以重新调整姿势再试。但如果用户连续失败太多次,系统就会回调 Error,并且直接关闭对话框——这时候你再怎么戳屏幕都没用了。
6.2 onAuthenticationError —— 遇到硬伤时的处理
这个方法是用来处理「不可恢复」的错误的。什么叫不可恢复?比如:
- 设备上没有虹膜传感器
- 传感器被遮挡或损坏
- 用户连续失败次数过多,系统进入锁定状态
- 用户主动按了返回键或取消按钮
每个错误都会带一个 errorCode,你可以根据这个 code 做不同的处理。我贴一段我项目里常用的代码:
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
super.onAuthenticationError(errorCode, errString);
// 我个人习惯把错误码打出来,方便调试
Log.e("BiometricCallback", "认证错误: " + errorCode + " - " + errString);
switch (errorCode) {
case BiometricPrompt.ERROR_HW_UNAVAILABLE:
// 硬件不可用,比如传感器被占用
showToast("虹膜传感器暂时不可用,请稍后再试");
break;
case BiometricPrompt.ERROR_LOCKOUT:
// 失败次数过多,被临时锁定(一般30秒)
showToast("尝试次数过多,请稍后再试");
break;
case BiometricPrompt.ERROR_LOCKOUT_PERMANENT:
// 永久锁定,必须用密码/图案解锁
showToast("已永久锁定,请使用密码验证");
// 这里我建议直接跳转到密码输入页面
navigateToPasswordScreen();
break;
case BiometricPrompt.ERROR_USER_CANCELED:
// 用户主动取消
Log.d("BiometricCallback", "用户取消了认证");
break;
default:
// 其他未知错误
showToast("认证出错: " + errString);
break;
}
}
onAuthenticationError 里弹了一个 DialogFragment,结果因为 Activity 已经处于 onPause 状态,直接崩溃了。所以建议你在处理 Error 时,先检查一下 Activity 的生命周期状态。
6.3 onAuthenticationSucceeded —— 认证成功的喜悦
这个最简单,也最让人开心。当虹膜匹配成功时,系统会回调这个方法。你只需要在这里做你真正想做的事——比如登录、支付、解锁。
@Override
public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
super.onAuthenticationSucceeded(result);
// 认证成功,可以放心的让用户进入下一步了
Log.d("BiometricCallback", "虹膜认证成功!");
// 这里可以获取认证方式(API 30+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
int authType = result.getAuthenticationType();
if (authType == BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC) {
Log.d("BiometricCallback", "通过生物特征认证");
} else if (authType == BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL) {
Log.d("BiometricCallback", "通过设备密码认证");
}
}
// 执行后续业务逻辑
onLoginSuccess();
}
这里有个小细节我想提一下。从 Android 11(API 30)开始,AuthenticationResult 里多了一个 getAuthenticationType() 方法。它可以告诉你用户到底是用生物特征(虹膜/指纹)通过的,还是用设备密码(PIN/图案)通过的。我个人觉得这个信息挺有用的,比如你可以根据认证方式决定是否展示不同的 UI 提示。
6.4 onAuthenticationFailed —— 别灰心,再试一次
这个回调表示「这次没对上,但你可以再试」。注意,它不会关闭对话框,也不会停止传感器工作。用户只需要调整一下姿势,系统会自动重新开始识别。
@Override
public void onAuthenticationFailed() {
super.onAuthenticationFailed();
// 提示用户重新尝试
Log.d("BiometricCallback", "认证失败,请重试");
// 我一般在这里做一个震动反馈,提醒用户
Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
if (vibrator != null && vibrator.hasVibrator()) {
vibrator.vibrate(100); // 震动100ms
}
// 更新UI提示
updateStatusText("虹膜识别失败,请调整姿势重试");
}
onAuthenticationFailed 里做一个「连续失败计数」。如果连续失败超过 3 次,我就主动调用 biometricPrompt.cancelAuthentication(),然后引导用户使用密码。这样可以避免用户因为反复失败而产生挫败感。
6.5 onAuthenticationResult —— 最终结果(API 30+)
这个方法是 Android 11 新增的。它会在认证流程彻底结束时被调用——不管成功还是失败。说白了,它就是一个「收尾」回调。
@Override
public void onAuthenticationResult(BiometricPrompt.AuthenticationResult result) {
super.onAuthenticationResult(result);
// 认证流程已结束,可以做一些清理工作
Log.d("BiometricCallback", "认证流程结束");
// 关闭对话框(如果还没关的话)
// 注意:正常情况下系统会自动关闭,但以防万一
if (biometricPrompt != null) {
biometricPrompt.cancelAuthentication();
}
// 释放资源
releaseResources();
}
嗯,说实话,这个回调在实际开发中用得不多。因为 onAuthenticationSucceeded 和 onAuthenticationError 已经能覆盖大部分场景了。但如果你需要在认证结束后统一做一些清理工作(比如释放相机资源、关闭传感器),那这个回调就派上用场了。
6.6 回调执行顺序与流程图
为了让你更直观地理解这几个回调的关系,我画了一张流程图。你可以把它当作一个「决策树」来看:
从这张图你可以看到:
- 认证开始后,系统会尝试匹配虹膜。
- 匹配成功 → 走
onAuthenticationSucceeded→ 流程结束。 - 匹配失败 → 走
onAuthenticationFailed→ 系统自动重试。 - 如果失败次数过多 → 走
onAuthenticationError→ 流程结束。 - 流程结束后,会调用
onAuthenticationResult(API 30+)。
6.7 避坑指南:我踩过的几个坑
最后,分享几个我在实际项目中遇到的坑,希望能帮你少走弯路:
我曾经在 onAuthenticationError 里直接弹了一个 Toast,结果因为 Activity 已经 onPause,直接报 Can't toast on a thread that has not called Looper.prepare()。后来我改成用 Handler 在主线程 post 一下,或者先检查 Activity 状态。
虽然这四个回调的 super 方法都是空实现,但 Android 官方文档建议你调用一下。万一以后系统版本升级,super 里加了什么逻辑,你不调用就可能出问题。我习惯在重写的第一个语句就写 super.xxx()。
这个我刚开始也搞混过。记住一句话:Failed 可以重试,Error 不能重试。如果你在 Failed 里关闭了对话框,那用户就没办法继续尝试了——这显然不是我们想要的效果。
好了,关于认证回调的内容就讲到这里。这四个方法其实不难,关键是要理解它们的触发时机和适用场景。下一节我们会聊一聊如何在实际项目中把这些回调组合起来,做一个完整的认证流程。
公众号:蓝海资料掘金营,微信deep3321