6、认证回调详解:onAuthenticationError、onAuthenticationSucceeded、onAuthenticationFailed、onAuthenticationResult

好,咱们接着聊。上一节我们把 BiometricPrompt 的实例给 new 出来了,也把对话框给弹起来了。但说实话,光会弹窗没用,你得知道用户到底有没有通过验证,对吧?

这就涉及到我们今天要讲的核心——认证回调。说白了,就是系统在认证完成后,会通过几个固定的方法告诉你结果。你只要把这些方法重写掉,在里面写你自己的业务逻辑就行了。

我个人习惯把回调分成两类:成功类失败/异常类。咱们一个一个来看。

6.1 回调接口总览

Android 官方提供了一个叫 BiometricPrompt.AuthenticationCallback 的抽象类。你创建 BiometricPrompt 实例的时候,必须传一个它的子类进去。这个类里有四个核心方法需要你关注:

方法名 触发时机 常见原因
onAuthenticationError 发生不可恢复的错误时 传感器硬件故障、用户取消次数过多、系统锁定
onAuthenticationSucceeded 认证成功时 虹膜/指纹匹配成功
onAuthenticationFailed 认证失败时(可重试) 虹膜/指纹不匹配
onAuthenticationResult 认证结果确定时 成功或失败后的最终回调(API 30+)

嗯,这里要注意一下。很多新手会把 onAuthenticationFailedonAuthenticationError 搞混。我简单解释一下:

  • 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("虹膜识别失败,请调整姿势重试");
}
💡 小技巧: 我在做支付类 App 时,会在 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();
}

嗯,说实话,这个回调在实际开发中用得不多。因为 onAuthenticationSucceededonAuthenticationError 已经能覆盖大部分场景了。但如果你需要在认证结束后统一做一些清理工作(比如释放相机资源、关闭传感器),那这个回调就派上用场了。

6.6 回调执行顺序与流程图

为了让你更直观地理解这几个回调的关系,我画了一张流程图。你可以把它当作一个「决策树」来看:

认证回调执行流程图 开始认证 匹配成功? onAuthenticationSucceeded onAuthenticationFailed 超过失败次数? 否(继续重试) onAuthenticationError 认证流程结束 onAuthenticationResult

从这张图你可以看到:

  • 认证开始后,系统会尝试匹配虹膜。
  • 匹配成功 → 走 onAuthenticationSucceeded → 流程结束。
  • 匹配失败 → 走 onAuthenticationFailed → 系统自动重试。
  • 如果失败次数过多 → 走 onAuthenticationError → 流程结束。
  • 流程结束后,会调用 onAuthenticationResult(API 30+)。

6.7 避坑指南:我踩过的几个坑

最后,分享几个我在实际项目中遇到的坑,希望能帮你少走弯路:

坑1:在 onAuthenticationError 里做了 UI 操作导致崩溃

我曾经在 onAuthenticationError 里直接弹了一个 Toast,结果因为 Activity 已经 onPause,直接报 Can't toast on a thread that has not called Looper.prepare()。后来我改成用 Handler 在主线程 post 一下,或者先检查 Activity 状态。

坑2:忘记调用 super 方法

虽然这四个回调的 super 方法都是空实现,但 Android 官方文档建议你调用一下。万一以后系统版本升级,super 里加了什么逻辑,你不调用就可能出问题。我习惯在重写的第一个语句就写 super.xxx()

坑3:混淆了 onAuthenticationFailed 和 onAuthenticationError

这个我刚开始也搞混过。记住一句话:Failed 可以重试,Error 不能重试。如果你在 Failed 里关闭了对话框,那用户就没办法继续尝试了——这显然不是我们想要的效果。

好了,关于认证回调的内容就讲到这里。这四个方法其实不难,关键是要理解它们的触发时机和适用场景。下一节我们会聊一聊如何在实际项目中把这些回调组合起来,做一个完整的认证流程。


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