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()
}
}
你想想看,这个流程其实很清晰。但我在代码审查时经常看到有人把通知逻辑直接写在回调里,又长又乱。我建议至少拆成三个方法:创建渠道、构建通知、发送通知。这样以后改起来也方便。
知识体系总览
下面这张图帮你理清本章的核心逻辑:
常见问题与避坑
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 通知发不出来 | 没有创建通知渠道,或渠道ID不匹配 | 检查 createNotificationChannel() 是否在发送前调用 |
| Android 12 崩溃 | PendingIntent 缺少可变性声明 | 加上 FLAG_IMMUTABLE 或 FLAG_MUTABLE |
| 通知没有声音/震动 | 渠道重要性设置太低 | 使用 IMPORTANCE_HIGH 或 IMPORTANCE_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要唯一。做到这三点,基本不会出大问题。