13、人脸识别与Activity生命周期:onResume启动认证、onPause取消认证、屏幕旋转处理

说实话,人脸识别认证跟Activity生命周期打交道这件事,很多新手容易翻车。我刚开始做的时候也觉得——不就是启动认证、取消认证嘛,有什么难的?结果第一次上线就被用户投诉:手机转个方向,人脸认证就卡死了。嗯,从那以后我再也不敢小看这个环节了。

今天咱们就聊聊,怎么把BiometricPrompt和Activity的生命周期玩明白。

为什么要在onResume启动认证?

你想想看,用户打开App,人脸认证弹出来,这时候如果Activity还没准备好——比如界面还在重建、数据还没恢复——那认证弹窗可能就显示异常,甚至直接崩溃。

onResume是Activity可见且可交互的信号。 我个人习惯把认证启动放在这里,因为这时候界面已经准备好了,用户也能看到弹窗。

核心原则: 认证启动必须保证Activity处于前台且可见状态。

@Override
protected void onResume() {
    super.onResume();
    // 我习惯在这里启动人脸认证
    if (shouldShowBiometricPrompt) {
        startBiometricAuth();
    }
}

这里有个细节——shouldShowBiometricPrompt这个标志位。为什么要加它?因为onResume可能会被多次调用,比如从别的Activity返回、屏幕旋转等。不加这个标志位,每次onResume都会弹认证,用户体验就很糟糕。

onPause取消认证,这个坑我踩过

onPause对应的是Activity即将失去焦点。说白了,用户可能按了Home键、接了个电话、或者跳转到其他页面。这时候如果人脸认证还在进行,会有什么问题?

  • 认证回调可能还在执行,但Activity已经不可见了
  • 弹窗可能显示在错误的界面上
  • 严重情况下会导致内存泄漏或崩溃

所以,在onPause中取消认证是必须的

@Override
protected void onPause() {
    super.onPause();
    // 我曾经忘记加这个,结果用户按Home键后认证弹窗还在
    cancelBiometricAuth();
}

private void cancelBiometricAuth() {
    if (biometricPrompt != null) {
        biometricPrompt.cancelAuthentication();
    }
}

注意: cancelAuthentication()调用后,BiometricPrompt会立即停止认证,并且不会再回调onAuthenticationError。所以如果你在onPause中取消了认证,后续的onResume中需要重新启动。

屏幕旋转处理——最容易被忽视的坑

屏幕旋转会导致Activity重建。这个过程会触发:
onPause → onStop → onDestroy → onCreate → onStart → onResume

如果处理不当,会出现什么情况?

  1. 旋转前认证弹窗正在显示
  2. Activity销毁重建
  3. 旧的BiometricPrompt实例失效
  4. 新的Activity没有重新启动认证
  5. 用户看到空白界面,认证卡死

我遇到过这个问题,当时用户反馈说「手机横过来就认证不了了」。排查了半天,发现就是屏幕旋转导致认证状态丢失。

解决方案: 在onSaveInstanceState中保存认证状态,在onRestoreInstanceState中恢复。

private static final String KEY_AUTH_IN_PROGRESS = "auth_in_progress";

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putBoolean(KEY_AUTH_IN_PROGRESS, isAuthInProgress);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    if (savedInstanceState != null) {
        isAuthInProgress = savedInstanceState.getBoolean(KEY_AUTH_IN_PROGRESS);
    }
}

@Override
protected void onResume() {
    super.onResume();
    if (isAuthInProgress) {
        startBiometricAuth();
    }
}

小技巧: 如果你不想让Activity在屏幕旋转时重建,可以在AndroidManifest中配置android:configChanges="orientation|screenSize"。但我不建议这么做,因为这样会失去系统对配置变化的自动处理能力。

完整的生命周期处理流程

下面这张图是我自己总结的,把整个流程串起来看会更清楚:

人脸认证与Activity生命周期交互流程 onResume() onPause() 屏幕旋转 启动BiometricPrompt 取消认证 保存认证状态 认证弹窗显示 弹窗消失 状态恢复 核心:onResume启动 → onPause取消 → 旋转时保存/恢复状态

避坑指南

我总结了几条实战中容易踩的坑,你写代码的时候可以留意一下:

场景 问题 解决方案
快速切换App onPause取消认证后,onResume又启动,导致频繁弹窗 加一个延迟标志位,比如500ms内不重复启动
多窗口模式 Activity进入多窗口,onPause被调用,认证取消 判断isInMultiWindowMode(),特殊处理
Fragment嵌套 Fragment的onResume/onPause与Activity不同步 在Fragment中单独管理认证生命周期
认证回调延迟 onPause中取消认证,但回调仍在执行 使用弱引用持有Activity,避免内存泄漏

特别提醒: 不要在onCreate或onStart中启动认证。这时候Activity还没完全可见,弹窗可能会显示异常。我见过有人这么写,结果在部分机型上弹窗位置错乱。

嗯,关于生命周期和人脸认证的配合,核心就这些。说白了就是三个字——对得上。启动和取消要对得上Activity的前后台状态,旋转前后的状态要对得上。做到这几点,基本就不会出大问题。


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