14、人脸识别与Fragment集成:在Fragment中使用BiometricPrompt、Fragment生命周期管理
说实话,很多开发者第一次接触生物识别时,都是在Activity里直接调用BiometricPrompt。这当然没问题,但现实项目里,我们更多是用Fragment来组织UI。毕竟现在单Activity架构已经是主流了,你想想看,Google官方都推了多少年了。
我个人习惯是把生物识别逻辑封装在Fragment里,这样复用性更好。但这里有个坑——Fragment的生命周期比Activity复杂得多。搞不好就会出现内存泄漏、回调丢失、甚至崩溃。今天我们就来聊聊怎么在Fragment里优雅地集成人脸识别。
14.1 Fragment中使用BiometricPrompt的两种方式
嗯,这里要分两种情况讨论:
- 方式一:Fragment直接创建BiometricPrompt实例
- 方式二:通过宿主Activity代理调用
我个人更推荐方式一,因为逻辑内聚,Fragment自己就能搞定一切。但要注意,BiometricPrompt的构造函数需要传入一个LifecycleOwner,而Fragment本身就是LifecycleOwner,所以直接传this就行。
核心要点:BiometricPrompt必须绑定到LifecycleOwner,否则在Fragment销毁后回调可能被触发,导致IllegalStateException。
14.2 代码示例:在Fragment中集成人脸识别
下面是我在实际项目中用过的模板,你直接拿去改改就能用:
public class FaceRecognitionFragment extends Fragment {
private BiometricPrompt biometricPrompt;
private BiometricPrompt.PromptInfo promptInfo;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initBiometricPrompt();
}
private void initBiometricPrompt() {
// 注意:这里使用Fragment作为LifecycleOwner
biometricPrompt = new BiometricPrompt(
this, // Fragment implements LifecycleOwner
new BiometricPrompt.AuthenticationCallback() {
@Override
public void onAuthenticationSucceeded(
@NonNull BiometricPrompt.AuthenticationResult result) {
// 识别成功
handleSuccess(result);
}
@Override
public void onAuthenticationError(int errorCode,
@NonNull CharSequence errString) {
// 识别错误
handleError(errorCode, errString);
}
@Override
public void onAuthenticationFailed() {
// 识别失败(非错误,只是没匹配上)
handleFailed();
}
}
);
promptInfo = new BiometricPrompt.PromptInfo.Builder()
.setTitle("人脸验证")
.setSubtitle("请面向手机")
.setNegativeButtonText("取消")
.setAllowedAuthenticators(
BiometricManager.Authenticators.BIOMETRIC_STRONG
| BiometricManager.Authenticators.BIOMETRIC_WEAK)
.build();
}
public void startAuthentication() {
if (biometricPrompt != null) {
biometricPrompt.authenticate(promptInfo);
}
}
private void handleSuccess(BiometricPrompt.AuthenticationResult result) {
// 处理成功逻辑
}
private void handleError(int errorCode, CharSequence errString) {
// 处理错误逻辑
}
private void handleFailed() {
// 处理失败逻辑
}
}
小技巧:我在项目中遇到过一个问题——Fragment被重建时,BiometricPrompt的callback会丢失。解决办法是在onSaveInstanceState里保存一个状态标记,重建后根据标记决定是否重新弹出认证。
14.3 Fragment生命周期管理:别让认证跑偏了
Fragment的生命周期比Activity复杂,主要体现在:
onCreateView和onDestroyView可能多次调用- Fragment可以 detached 再 reattached
- 后台Fragment可能被系统回收
我曾经踩过一个坑:在onCreateView里初始化BiometricPrompt,结果Fragment被添加到回退栈后又返回,导致重复初始化。后来我改成在onCreate里初始化,问题就解决了。
14.4 生命周期与BiometricPrompt的绑定关系
这里我画了一张图,帮你理清关系:
14.5 避坑指南:Fragment中的人脸识别常见问题
我总结了几条血泪教训,你记一下:
警告:千万不要在onActivityCreated里调用authenticate()。我曾经这么干过,结果Fragment还没完全可见,BiometricPrompt就弹出来了,用户一脸懵。
| 问题场景 | 原因 | 解决方案 |
|---|---|---|
| Fragment回退后认证回调丢失 | Fragment被detach,LifecycleOwner失效 | 使用requireActivity()作为LifecycleOwner |
| 多次弹出认证对话框 | Fragment重建导致重复初始化 | 在onCreate中初始化,用标志位控制 |
| 认证成功后UI未更新 | 回调在后台线程,未切回主线程 | 使用requireActivity().runOnUiThread() |
| 内存泄漏 | BiometricPrompt持有Fragment引用 | 在onDestroy中置空 |
14.6 最佳实践:封装一个可复用的BiometricFragment
嗯,这里我分享一个我自己项目里用的封装思路。说白了就是做一个基类,所有需要人脸识别的Fragment都继承它:
public abstract class BaseBiometricFragment extends Fragment {
private BiometricPrompt biometricPrompt;
private boolean isAuthenticating = false;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initBiometricPrompt();
}
private void initBiometricPrompt() {
biometricPrompt = new BiometricPrompt(
this,
new BiometricPrompt.AuthenticationCallback() {
@Override
public void onAuthenticationSucceeded(
@NonNull BiometricPrompt.AuthenticationResult result) {
isAuthenticating = false;
onBiometricSuccess(result);
}
@Override
public void onAuthenticationError(int errorCode,
@NonNull CharSequence errString) {
isAuthenticating = false;
onBiometricError(errorCode, errString);
}
@Override
public void onAuthenticationFailed() {
onBiometricFailed();
}
}
);
}
protected void startBiometricAuth(BiometricPrompt.PromptInfo info) {
if (!isAuthenticating && biometricPrompt != null) {
isAuthenticating = true;
biometricPrompt.authenticate(info);
}
}
// 子类实现具体业务逻辑
protected abstract void onBiometricSuccess(
BiometricPrompt.AuthenticationResult result);
protected abstract void onBiometricError(
int errorCode, CharSequence errString);
protected abstract void onBiometricFailed();
@Override
public void onDestroy() {
super.onDestroy();
biometricPrompt = null; // 防止内存泄漏
}
}
个人经验:我建议你在onStop里也做一次清理。因为Fragment可能只是不可见,但还没销毁。这时候如果认证还在进行,用户切到其他应用再回来,状态就乱了。
14.7 总结一下
Fragment里集成人脸识别,核心就三点:
- 生命周期绑定:BiometricPrompt必须和Fragment的LifecycleOwner绑定
- 初始化时机:在
onCreate里初始化,不要在onCreateView里 - 状态管理:用标志位控制认证流程,避免重复触发
说白了,只要把生命周期管好了,人脸识别在Fragment里跑起来跟Activity里一样稳。我刚开始做的时候也踩过不少坑,但后来总结出这套模式,项目里用了两年多没出过问题。
公众号:蓝海资料掘金营,微信deep3321