8. 用户取消处理:用户主动取消、系统取消、重试机制、友好的UI提示
嗯,这一章咱们聊聊取消处理。你可能会想,取消不就是点一下“取消”按钮吗?有什么好讲的?
其实不然。我在项目里见过太多因为取消处理不当导致的崩溃、ANR,甚至用户直接给一星差评。说白了,取消处理是用户体验的最后一道防线,处理不好,前面所有努力都白费。
8.1 用户主动取消 vs 系统取消
先分清楚两种取消场景:
| 取消类型 | 触发方式 | 典型场景 |
|---|---|---|
| 用户主动取消 | 用户点击“取消”按钮或按返回键 | 用户突然不想验证了,或者误触了验证流程 |
| 系统取消 | 系统内部中断(如锁屏、来电、低内存) | 验证过程中突然来电话,或者系统资源紧张被回收 |
我个人习惯把这两种情况分开处理。为什么呢?因为用户主动取消,通常意味着用户有明确的意图;而系统取消,用户可能还一脸懵,需要更友好的引导。
核心原则:用户主动取消,尊重用户选择,直接退出流程;系统取消,保留上下文,给用户重试的机会。
8.2 用户主动取消的处理
用户点击取消,或者按返回键,这时候应该做什么?
- 立即停止认证:调用
BiometricPrompt.cancelAuthentication(),释放系统资源。 - 不要弹确认对话框:用户已经明确要取消,再弹“你确定要取消吗?”只会让用户烦躁。我见过有些App这么干,结果用户更生气了。
- 回调处理:在
onAuthenticationError()中捕获ERROR_USER_CANCELED(错误码13),执行退出逻辑。
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
if (errorCode == BiometricPrompt.ERROR_USER_CANCELED) {
// 用户主动取消,直接关闭页面
finish();
} else {
// 其他错误,走系统取消逻辑
handleSystemCancel(errorCode, errString);
}
}
小技巧:如果用户连续多次主动取消,可以考虑记录一下次数。我曾经遇到一个用户,因为指纹识别总失败,连续取消了10次。后来我们加了一个“遇到问题?试试密码登录”的入口,用户满意度提升了不少。
8.3 系统取消的处理
系统取消的情况比较复杂。常见的系统取消原因包括:
- 锁屏:用户按了电源键,屏幕关闭。
- 来电:电话呼入,系统抢占前台。
- 低内存:系统回收了BiometricPrompt的资源。
- 超时:用户长时间未操作,系统自动取消。
对于系统取消,我的建议是:不要直接退出。用户可能只是暂时离开,或者被其他事情打断。你应该保留当前页面,并显示一个友好的提示。
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
switch (errorCode) {
case BiometricPrompt.ERROR_LOCKOUT:
// 锁定,需要等待
showLockoutMessage(errString);
break;
case BiometricPrompt.ERROR_TIMEOUT:
// 超时,提示用户重新尝试
showRetryPrompt("验证超时,请重试");
break;
case BiometricPrompt.ERROR_CANCELED:
// 系统取消(非用户主动),保留页面
showMessage("验证已取消,可重新尝试");
break;
default:
// 其他系统错误,记录日志
Log.w("Biometric", "系统取消: " + errorCode + " - " + errString);
showRetryPrompt("系统异常,请重试");
}
}
注意:系统取消时,不要自动重新开始认证。我曾经犯过这个错误——系统取消后立刻自动重试,结果用户还没反应过来,又取消了,陷入死循环。正确的做法是:显示一个重试按钮,让用户主动点击。
8.4 重试机制的设计
重试机制是取消处理的核心。设计得好,用户会觉得“这个App很智能”;设计得不好,用户会觉得“这个App在耍我”。
我总结了几条重试原则:
- 有限重试:不要无限重试。一般3次失败后,引导用户使用其他方式(如密码)。
- 冷却时间:连续失败后,增加重试间隔。比如第一次失败立即重试,第二次等待2秒,第三次等待5秒。
- 状态保留:重试时不要重置整个页面,只重置认证状态。用户输入的其他信息(如果有)应该保留。
private int retryCount = 0;
private static final int MAX_RETRIES = 3;
private void handleRetry() {
if (retryCount < MAX_RETRIES) {
retryCount++;
// 显示重试按钮,用户点击后重新启动认证
showRetryButton("重试 (" + (MAX_RETRIES - retryCount + 1) + "/" + MAX_RETRIES + ")");
} else {
// 超过最大重试次数,引导用户使用其他方式
showAlternativeLoginOption();
}
}
避坑指南:我曾经在重试时忘记重置 CancellationSignal,导致第二次认证直接失败。记住:每次重新认证前,必须创建新的 BiometricPrompt 实例和新的 CancellationSignal。
8.5 友好的UI提示
UI提示是取消处理的最后一步,也是最容易被忽视的一步。很多App在取消时只显示一个Toast“验证已取消”,然后就没了。这其实不够。
我建议的UI提示策略:
- 明确原因:告诉用户为什么取消。比如“验证超时,请重试”比“验证失败”好得多。
- 提供下一步:取消后不要留用户在那里发呆。提供“重试”、“使用密码”、“联系客服”等选项。
- 保持一致性:UI风格与App整体保持一致。不要突然弹出一个系统风格的Dialog,显得很突兀。
下面是一个友好的UI提示示例:
private void showRetryPrompt(String message) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("验证中断")
.setMessage(message + "\n您可以重试,或使用其他方式登录。")
.setPositiveButton("重试", (dialog, which) -> {
startBiometricAuthentication();
})
.setNegativeButton("使用密码", (dialog, which) -> {
navigateToPasswordLogin();
})
.setCancelable(false) // 防止用户再次取消
.show();
}
个人经验:在UI提示中,尽量使用“您”而不是“你”。虽然只是一个字的差别,但用户感知到的尊重感完全不同。另外,按钮文字用“重试”而不是“确定”,因为“确定”太模糊了,用户不知道确定什么。
8.6 知识体系流程图
下面这张图总结了取消处理的整体逻辑,你可以对照着看:
嗯,这张图把整个取消处理的脉络理清楚了。你想想看,从认证开始,到取消发生,再到分支处理,最后到重试或结束,每一步都有对应的代码和UI策略。
8.7 总结
取消处理看似简单,但细节很多。我最后再强调几点:
- 区分主动取消和系统取消,处理方式完全不同。
- 重试要有限制,不要无限循环。
- UI提示要友好,告诉用户原因,并提供下一步操作。
- 记得重置状态,特别是
CancellationSignal和BiometricPrompt实例。
最后提醒:千万不要在 onAuthenticationError 中做耗时操作,比如网络请求。这个回调是在主线程执行的,阻塞会导致ANR。如果你需要记录日志或上报数据,请使用异步方式。
好了,这一章就到这里。取消处理是用户体验的“兜底”环节,做好了,用户会觉得你的App很可靠;做不好,用户会觉得你的App很“脆弱”。希望你能把这些经验用在自己的项目里。