18、指纹认证与通知栏:创建通知渠道、显示认证通知、通知点击处理、锁屏状态下的认证

通知栏和指纹认证,这两个功能单独看都不算复杂。但把它们结合起来,嗯,这里就有不少门道了。

我个人习惯把这种组合场景叫做「锁屏下的快捷操作」。用户手机锁着,收到一条通知,点一下,指纹一按,直接跳转到应用内部某个敏感页面。这个流程做得好,用户体验会非常丝滑;做得不好,用户可能连通知都看不到。

今天我们就来拆解这个流程。说白了,就是三件事:通知怎么发、通知怎么点、点了之后怎么认证

18.1 创建通知渠道:Android 8.0 的必修课

从 Android 8.0(API 26)开始,所有通知都必须绑定一个通知渠道(NotificationChannel)。如果你不创建渠道,通知根本不会显示。我在项目中遇到过好几次这种问题——测试机是 Android 7.0 没事,换到 Android 9.0 就收不到通知,排查了半天才发现是渠道没建。

创建渠道的代码其实很简单,但有几个细节要注意:

// 创建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    String channelId = "fingerprint_auth_channel";
    String channelName = "指纹认证通知";
    String channelDescription = "用于显示指纹认证相关的通知";
    
    NotificationChannel channel = new NotificationChannel(
        channelId,
        channelName,
        NotificationManager.IMPORTANCE_HIGH  // 重要级别
    );
    channel.setDescription(channelDescription);
    channel.setLockscreenVisibility(NotificationCompat.VISIBILITY_PUBLIC);
    
    NotificationManager notificationManager = 
        getSystemService(NotificationManager.class);
    notificationManager.createNotificationChannel(channel);
}

这里有个关键点:setLockscreenVisibility 方法。它决定了通知在锁屏界面上的显示方式。

可见性级别 含义 适用场景
VISIBILITY_PUBLIC 锁屏上显示全部内容 普通通知,无敏感信息
VISIBILITY_PRIVATE 锁屏上只显示图标,隐藏内容 包含用户隐私的通知
VISIBILITY_SECRET 锁屏上完全不显示 高度敏感的通知

对于指纹认证场景,我建议使用 VISIBILITY_PUBLIC。为什么呢?因为用户需要在锁屏状态下看到通知内容,才能决定要不要点击认证。如果你设成 PRIVATE,用户解锁后还得再点一次通知,多了一步操作,体验就差了。

注意:渠道创建只需要在应用第一次启动时执行一次。如果后续修改了渠道配置(比如改了名称或重要级别),需要先删除旧渠道再重新创建,否则修改不会生效。

18.2 显示认证通知:带上 PendingIntent

通知本身没什么特别的,但我们要在通知上绑定一个 PendingIntent,这样用户点击通知时才能触发指纹认证流程。

// 构建通知
Intent intent = new Intent(this, FingerprintAuthActivity.class);
intent.putExtra("need_auth", true);  // 标记需要指纹认证

PendingIntent pendingIntent = PendingIntent.getActivity(
    this,
    0,
    intent,
    PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);

NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId)
    .setSmallIcon(R.drawable.ic_fingerprint)
    .setContentTitle("请验证指纹")
    .setContentText("点击此处进行指纹认证")
    .setPriority(NotificationCompat.PRIORITY_HIGH)
    .setContentIntent(pendingIntent)
    .setAutoCancel(true);  // 点击后自动移除通知

NotificationManagerCompat notificationManager = 
    NotificationManagerCompat.from(this);
notificationManager.notify(1001, builder.build());

这里有个小坑:FLAG_IMMUTABLE 是 Android 12 之后强制要求的。如果你不加上这个 flag,在 Android 12 以上设备上会直接崩溃。我曾经在适配 Android 12 时踩过这个坑,日志里报了个 SecurityException,查了半天才发现是 PendingIntent 的问题。

18.3 通知点击处理:跳转与认证的衔接

用户点击通知后,会跳转到我们指定的 Activity。但这个 Activity 不能直接展示内容,它要先走一遍指纹认证流程。

我的做法是这样的:在 Activity 的 onCreate 方法中判断 Intent 是否携带了认证标记,如果有,就立即启动指纹认证。

public class FingerprintAuthActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        boolean needAuth = getIntent().getBooleanExtra("need_auth", false);
        if (needAuth) {
            // 显示指纹认证界面
            showFingerprintDialog();
        } else {
            // 正常启动,不要求认证
            showNormalContent();
        }
    }
    
    private void showFingerprintDialog() {
        // 这里调用我们之前章节讲过的 BiometricPrompt
        BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
            .setTitle("指纹认证")
            .setSubtitle("请验证您的指纹以查看内容")
            .setNegativeButtonText("取消")
            .build();
            
        BiometricPrompt biometricPrompt = new BiometricPrompt(
            this, 
            ContextCompat.getMainExecutor(this),
            new BiometricPrompt.AuthenticationCallback() {
                @Override
                public void onAuthenticationSucceeded(
                        BiometricPrompt.AuthenticationResult result) {
                    // 认证成功,显示内容
                    showSensitiveContent();
                }
                
                @Override
                public void onAuthenticationError(int errorCode, 
                        CharSequence errString) {
                    // 认证失败,关闭页面
                    finish();
                }
            }
        );
        
        biometricPrompt.authenticate(promptInfo);
    }
}

你想想看,这个流程其实很自然:用户点击通知 → 进入 Activity → 弹出指纹对话框 → 认证通过 → 显示内容。每一步都是用户主动触发的,不会有突兀感。

18.4 锁屏状态下的认证:特殊处理

锁屏状态下调用指纹认证,有个很头疼的问题:BiometricPrompt 的对话框可能被系统锁屏界面挡住

为什么会这样?因为 BiometricPrompt 本质上是一个系统级别的对话框,它和锁屏界面属于同一个窗口层级。如果手机处于锁屏状态,系统会优先显示锁屏界面,你的指纹对话框可能被压在下面,用户根本看不到。

我在项目中遇到过这个情况:测试的时候手机亮着屏,指纹对话框弹得好好的。结果把手机锁屏再试,通知点进去后一片漆黑,用户不知道要干嘛。后来才发现是锁屏界面把对话框挡住了。

解决方案有两种:

  • 方案一:先解锁再认证
    在启动 Activity 时,先请求用户解锁屏幕。解锁成功后,再弹出指纹对话框。
  • 方案二:使用 KeyguardManager
    通过 KeyguardManager 判断当前是否处于锁屏状态,如果是,先解除锁屏再认证。

我个人更推荐方案一,因为它更符合用户预期。具体做法是在 Activity 的 onCreate 中加上锁屏检测:

KeyguardManager keyguardManager = 
    (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);

if (keyguardManager.isKeyguardLocked()) {
    // 手机处于锁屏状态,先请求解锁
    Intent unlockIntent = keyguardManager.createConfirmDeviceCredentialIntent(
        "解锁手机", 
        "请先解锁以进行指纹认证"
    );
    if (unlockIntent != null) {
        startActivityForResult(unlockIntent, REQUEST_CODE_UNLOCK);
    }
} else {
    // 手机已解锁,直接弹出指纹认证
    showFingerprintDialog();
}

这里要注意:createConfirmDeviceCredentialIntent 会弹出一个系统级的解锁界面,用户输入密码或图案解锁后,会通过 onActivityResult 返回结果。我们在 onActivityResult 中判断解锁成功后再启动指纹认证。

小技巧:如果用户已经设置了指纹解锁手机,那么解锁界面会直接提示用户按指纹。这种情况下,你其实不需要再额外调用 BiometricPrompt,因为系统已经帮你完成了指纹认证。你只需要在解锁成功后直接显示内容即可。

18.5 整体流程梳理

说了这么多,我们来画一张图,把整个流程串起来。这样你看起来会更清楚。

指纹认证与通知栏整体流程 用户收到通知 点击通知 判断是否处于锁屏状态 是(锁屏) 否(已解锁) 请求解锁屏幕 弹出指纹认证对话框 解锁成功

从这张图可以看得很清楚:整个流程的核心就是「通知 → 点击 → 锁屏判断 → 认证 → 显示内容」。每一步都是环环相扣的,任何一个环节出问题,用户体验都会打折扣。

最后提醒一句:锁屏状态下的指纹认证一定要做充分测试。不同厂商的 Android 系统对锁屏和 BiometricPrompt 的处理方式可能不一样。我建议你在至少 3-5 款不同品牌的手机上测试这个流程,确保万无一失。

核心要点回顾:

  • 通知渠道必须设置锁屏可见性为 PUBLIC
  • PendingIntent 要加上 FLAG_IMMUTABLE
  • 锁屏状态下先请求解锁,再弹出指纹认证
  • 多机型测试,避免厂商差异导致的问题

好了,这一章的内容就到这里。指纹认证与通知栏的结合,说白了就是让用户在锁屏状态下也能安全快捷地访问敏感内容。把握好「安全」和「便捷」的平衡点,你的应用就能给用户带来很好的体验。


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