4. BiometricPrompt基础用法:创建实例、设置回调、启动认证
好,我们直接进入正题。
上一章我们聊了前置准备,权限、Intent、还有那个坑爹的ActivityResultLauncher。现在,假设你已经把那些都搞定了,该有的权限都给了,该弹的弹窗也弹了。接下来,就是真正的核心——BiometricPrompt 本身。
说白了,BiometricPrompt 就是 Android 官方给我们封装好的一个“生物识别对话框”。你不用自己去画指纹动画,也不用自己去调人脸摄像头。你只需要告诉它:“嘿,我要开始验证了,结果你通知我。” 剩下的,系统帮你搞定。
我个人习惯把 BiometricPrompt 的用法拆成三步:创建实例 → 设置回调 → 启动认证。嗯,就这么简单。但每一步里都有细节,不注意就会翻车。
创建 BiometricPrompt 对象 → 绑定 AuthenticationCallback → 调用 authenticate() 方法。
系统会弹出官方对话框,用户完成人脸/指纹验证后,回调告诉你结果。
4.1 创建 BiometricPrompt 实例
先看代码。这是最基础的一个创建方式,我一般写在 Activity 或者 Fragment 的初始化阶段。
// 记得在成员变量区声明
private BiometricPrompt biometricPrompt;
private BiometricPrompt.PromptInfo promptInfo;
// 在 onCreate 或 onViewCreated 中初始化
private void initBiometricPrompt() {
// 1. 创建 PromptInfo —— 这是对话框的配置
promptInfo = new BiometricPrompt.PromptInfo.Builder()
.setTitle("人脸验证")
.setSubtitle("请面向手机完成验证")
.setDescription("用于登录您的账户")
.setNegativeButtonText("取消")
.build();
// 2. 创建 BiometricPrompt 实例 —— 需要传入一个回调
biometricPrompt = new BiometricPrompt(
this, // Activity 或 Fragment
Executors.newSingleThreadExecutor(), // 回调执行的线程
new BiometricPrompt.AuthenticationCallback() {
// 回调内容我们下一节细讲
}
);
}
这里有个点我想强调一下。第二个参数是 Executor,它决定了你的回调方法在哪个线程执行。我建议用 Executors.newSingleThreadExecutor() 或者主线程的 ContextCompat.getMainExecutor(this)。
4.2 设置 AuthenticationCallback
这个回调是 BiometricPrompt 的灵魂。系统验证完人脸或指纹后,结果全在这里告诉你。
我直接贴一个完整的回调实现,然后逐一说每个方法。
new BiometricPrompt.AuthenticationCallback() {
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
// 认证出错 —— 比如用户取消、系统不支持、被锁定了
// errorCode 可以帮你判断具体原因
Log.e("Biometric", "认证错误: " + errString);
// 我曾经遇到过 errorCode == BiometricPrompt.ERROR_NEGATIVE_BUTTON
// 其实就是用户点了“取消”按钮,别当成真正的错误去弹 Toast
}
@Override
public void onAuthenticationSucceeded(
BiometricPrompt.AuthenticationResult result) {
// 认证成功!
// result 里可以拿到 BiometricType(人脸还是指纹)
Log.d("Biometric", "认证成功");
// 这里做你的业务逻辑,比如登录、支付
}
@Override
public void onAuthenticationFailed() {
// 认证失败 —— 人脸没匹配上,或者指纹不对
// 注意:这个回调不会关闭对话框,用户可以继续尝试
Log.w("Biometric", "认证失败,请重试");
// 我一般在这里更新 UI 提示“请再试一次”
}
}
嗯,这里要注意几个容易混淆的点。
- onAuthenticationError:一旦触发,对话框会关闭,认证流程结束。你需要重新调用
authenticate()才能再次启动。 - onAuthenticationFailed:对话框不会关闭,用户可以继续尝试。说白了就是“这次没对上,再来一次”。
- onAuthenticationSucceeded:成功,对话框自动关闭。你可以拿到
AuthenticationResult,里面有个getBiometricType()方法,可以判断用户用的是人脸还是指纹。
onAuthenticationError 里直接弹 Toast 提示“系统错误”。结果用户点了“取消”按钮,也弹了个“系统错误”,用户一脸懵。后来我才发现,ERROR_NEGATIVE_BUTTON 就是用户主动取消,不应该算错误。所以一定要判断 errorCode。
4.3 启动认证
实例有了,回调有了,最后一步就是启动。代码就一行:
// 启动人脸认证
biometricPrompt.authenticate(promptInfo);
就这么简单?对,就这么简单。系统会立刻弹出官方的人脸验证对话框,开始扫描用户的脸。
但这里有一个非常重要的前提:你必须先检查设备是否支持人脸识别,以及用户是否已经录入人脸。否则你直接调用 authenticate(),系统会直接回调 onAuthenticationError,告诉你“没有可用的生物识别方式”。
我建议在调用 authenticate() 之前,先做一次检查:
BiometricManager biometricManager = BiometricManager.from(this);
int canAuthenticate = biometricManager.canAuthenticate(
BiometricManager.Authenticators.BIOMETRIC_STRONG
);
if (canAuthenticate == BiometricManager.BIOMETRIC_SUCCESS) {
// 设备支持,且已录入生物信息
biometricPrompt.authenticate(promptInfo);
} else {
// 处理不支持或未录入的情况
// 比如跳转到系统设置页面
}
这个检查逻辑,我在项目里是放在一个工具类里的。每次调用认证前都跑一遍,避免无意义的弹窗。
4.4 完整代码示例
把上面几段拼起来,就是一个完整的 BiometricPrompt 基础用法。我贴一个我常用的模板:
public class FaceAuthActivity extends AppCompatActivity {
private BiometricPrompt biometricPrompt;
private BiometricPrompt.PromptInfo promptInfo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_face_auth);
// 1. 配置对话框
promptInfo = new BiometricPrompt.PromptInfo.Builder()
.setTitle("人脸验证")
.setSubtitle("请面向手机")
.setDescription("用于登录")
.setNegativeButtonText("取消")
.build();
// 2. 创建实例
biometricPrompt = new BiometricPrompt(this,
ContextCompat.getMainExecutor(this),
new BiometricPrompt.AuthenticationCallback() {
@Override
public void onAuthenticationSucceeded(
BiometricPrompt.AuthenticationResult result) {
// 成功:跳转主页
startActivity(new Intent(FaceAuthActivity.this, MainActivity.class));
finish();
}
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
// 错误:根据 errorCode 做不同处理
if (errorCode == BiometricPrompt.ERROR_NEGATIVE_BUTTON) {
// 用户取消,什么都不做
} else {
Toast.makeText(FaceAuthActivity.this, "错误: " + errString, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onAuthenticationFailed() {
// 失败:提示用户重试
Toast.makeText(FaceAuthActivity.this, "人脸不匹配,请重试", Toast.LENGTH_SHORT).show();
}
});
// 3. 检查并启动
findViewById(R.id.btn_start_auth).setOnClickListener(v -> {
BiometricManager manager = BiometricManager.from(this);
if (manager.canAuthenticate(
BiometricManager.Authenticators.BIOMETRIC_STRONG) == BiometricManager.BIOMETRIC_SUCCESS) {
biometricPrompt.authenticate(promptInfo);
} else {
Toast.makeText(this, "设备不支持或未录入人脸", Toast.LENGTH_SHORT).show();
}
});
}
}
4.5 几个容易忽略的细节
| 要点 | 说明 |
|---|---|
| 对话框不可自定义 UI | BiometricPrompt 的对话框是系统级的,你不能改它的布局。想换样式?没门。但好处是统一、安全。 |
| 回调线程 | 第二个参数 Executor 决定了回调线程。主线程更新 UI 方便,但别做耗时操作。 |
| 取消按钮 | 必须设置 setNegativeButtonText(),否则会抛异常。这是强制要求。 |
| 多次调用 | 如果用户连续点击“启动认证”,不要重复调用 authenticate()。我一般加一个布尔锁,防止重复弹窗。 |
onAuthenticationFailed() 里延迟 1 秒再次调用 authenticate()。但别无限循环,我一般限制最多重试 3 次。
好了,BiometricPrompt 的基础用法就这些。创建实例、设置回调、启动认证,三步走。你想想看,是不是比自己去调摄像头简单多了?
下一章我们会深入 PromptInfo 的配置细节,比如怎么隐藏“取消”按钮、怎么支持设备凭据(PIN/图案)作为备选。嗯,那些才是真正让用户体验变好的地方。