24、人脸识别与通知:认证成功后发送通知、通知渠道设置、PendingIntent安全处理

各位同学,今天我们来聊一个很实际的问题——人脸识别成功后,怎么给用户发个通知?

你想想看,用户刷脸成功了,总得有点反馈吧。光在App里弹个Toast,用户切到后台就看不到了。这时候,系统通知就派上用场了。我在项目里见过不少团队,人脸识别做得很溜,但通知这块处理得马马虎虎,结果用户反馈说「我明明认证成功了,怎么没任何提示?」——说白了,就是通知没发对。

通知渠道:Android 8.0 之后的必修课

从 Android 8.0(API 26)开始,Google 强制要求所有通知必须绑定通知渠道(NotificationChannel)。没有渠道,通知根本发不出去。嗯,这里要注意,很多新手会踩这个坑。

我个人习惯是在 Application 初始化时就把渠道建好。为什么?因为渠道只需要创建一次,而且必须在发送任何通知之前完成。

// 创建通知渠道
private fun createNotificationChannel() {
    // Android 8.0 及以上才需要
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channelId = "face_auth_channel"
        val channelName = "人脸认证通知"
        val importance = NotificationManager.IMPORTANCE_HIGH
        
        val channel = NotificationChannel(channelId, channelName, importance).apply {
            description = "用于展示人脸认证结果的通知"
            enableVibration(true)
            setShowBadge(true)
        }
        
        val notificationManager = getSystemService(NotificationManager::class.java)
        notificationManager.createNotificationChannel(channel)
    }
}

重要提醒:通知渠道一旦创建,名称和重要性级别就不能再修改。如果你想改,只能卸载App重装,或者换个渠道ID。所以上线前一定要想清楚。

我在项目中遇到过一件事:有个版本把渠道重要性设成了 IMPORTANCE_LOW,结果用户收不到声音和震动,以为是Bug。后来我们只能发版升级,换了个新渠道ID才解决。所以,渠道设计要慎重

PendingIntent:安全是第一要务

通知一般都会带点击事件,比如点击后跳转到某个Activity。这个点击事件就是用 PendingIntent 来实现的。

但是!PendingIntent 有个安全隐患——如果处理不当,恶意应用可能拦截或篡改你的 Intent。我记得 Android 12 之后,Google 对 PendingIntent 的安全性要求更严格了。

怎么处理?核心就两点:

  • 必须指定 FLAG_IMMUTABLE 或 FLAG_MUTABLE
  • 能设成 IMMUTABLE 就别用 MUTABLE
// 安全的 PendingIntent 写法
val intent = Intent(this, AuthResultActivity::class.java).apply {
    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
    putExtra("auth_result", "success")
}

// Android 12+ 必须显式指定可变性
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
} else {
    PendingIntent.FLAG_UPDATE_CURRENT
}

val pendingIntent = PendingIntent.getActivity(
    this,
    1001,  // requestCode,用于区分不同的 PendingIntent
    intent,
    flags
)

避坑指南:我曾经在项目里忘了加 FLAG_IMMUTABLE,结果在 Android 12 设备上直接崩溃,日志显示 IllegalArgumentException: Targeting S+ requires ...。后来排查才发现是 PendingIntent 的锅。所以,Android 12 及以上,必须显式声明可变性

人脸认证成功后发送通知

好了,渠道有了,PendingIntent 也安全了。接下来就是组合起来,在人脸认证成功后发通知。

我个人习惯把通知构建逻辑封装成一个方法,这样代码更干净。

// 发送人脸认证成功通知
private fun sendAuthSuccessNotification() {
    val channelId = "face_auth_channel"
    val notificationId = System.currentTimeMillis().toInt()  // 唯一ID
    
    // 构建通知
    val builder = NotificationCompat.Builder(this, channelId)
        .setSmallIcon(R.drawable.ic_face_success)
        .setContentTitle("人脸认证成功")
        .setContentText("您已通过人脸识别验证")
        .setPriority(NotificationCompat.PRIORITY_HIGH)
        .setAutoCancel(true)  // 点击后自动移除
        .setContentIntent(pendingIntent)  // 上面创建的 PendingIntent
        .setCategory(NotificationCompat.CATEGORY_STATUS)
    
    // 发送通知
    val notificationManager = NotificationManagerCompat.from(this)
    notificationManager.notify(notificationId, builder.build())
}

小技巧:notificationId 最好用时间戳取整,避免重复。如果两个通知用同一个ID,后面的会覆盖前面的。我一般用 System.currentTimeMillis().toInt(),基本不会重复。

完整流程:从认证到通知

把上面所有知识点串起来,一个完整的人脸认证+通知流程大概是这样的:

// 在 Activity 或 Fragment 中
class AuthActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_auth)
        
        // 1. 创建通知渠道(只需一次)
        createNotificationChannel()
        
        // 2. 开始人脸认证
        startFaceAuth()
    }
    
    private fun startFaceAuth() {
        // 这里调用你的人脸识别 SDK
        // 假设认证成功后回调 onAuthSuccess()
    }
    
    private fun onAuthSuccess() {
        // 3. 认证成功,发送通知
        sendAuthSuccessNotification()
        
        // 4. 可选:更新UI
        showSuccessUI()
    }
}

你想想看,这个流程其实很清晰。但我在代码审查时经常看到有人把通知逻辑直接写在回调里,又长又乱。我建议至少拆成三个方法:创建渠道、构建通知、发送通知。这样以后改起来也方便。

知识体系总览

下面这张图帮你理清本章的核心逻辑:

人脸识别 + 通知 核心流程 人脸认证成功 创建通知渠道 PendingIntent 构建通知对象 发送通知 用户点击 → 跳转Activity ⚠️ 必须指定 FLAG_IMMUTABLE ✅ 渠道创建后不可修改

常见问题与避坑

问题 原因 解决方案
通知发不出来 没有创建通知渠道,或渠道ID不匹配 检查 createNotificationChannel() 是否在发送前调用
Android 12 崩溃 PendingIntent 缺少可变性声明 加上 FLAG_IMMUTABLEFLAG_MUTABLE
通知没有声音/震动 渠道重要性设置太低 使用 IMPORTANCE_HIGHIMPORTANCE_DEFAULT
点击通知没反应 PendingIntent 的 Intent 缺少 FLAG_ACTIVITY_NEW_TASK 给 Intent 加上 FLAG_ACTIVITY_NEW_TASK

我的个人建议:通知渠道的ID最好定义成常量,比如 const val CHANNEL_ID_FACE_AUTH = "face_auth_channel"。这样在多个地方引用时不会写错。我曾经因为手抖把渠道ID写成了 "face_auth_chanel"(少了个n),排查了半天才发现。

好了,关于人脸识别与通知的整合,核心就是这些。记住三个关键点:渠道要提前建、PendingIntent 要安全、通知ID要唯一。做到这三点,基本不会出大问题。

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