上一篇讲了进程生命周期,这篇聊一个开发者天天碰但很少深入的主题——通知。Android 8.0 引入 Channel 后,通知管理发生了质的变化。通知样式、优先级、分组、权限,每一步都有坑。
NotificationChannel 是什么
Android 8.0(API 26)之前,通知直接发就行,优先级通过 NotificationCompat.PRIORITY_* 设置。8.0 之后必须创建 Channel,用户可以在系统设置里单独控制每个 Channel 的开关、声音、振动。
val channel = NotificationChannel(
"message_channel",
"聊天消息",
NotificationManager.IMPORTANCE_HIGH
).apply {
description = "新消息通知"
enableVibration(true)
enableLights(true)
lightColor = Color.RED
}
NotificationManagerCompat.from(context)
.createNotificationChannel(channel)
IMPORTANCE 级别:
重要一点:Channel 一旦创建,IMPORTANCE 级别就不能通过代码修改了,只有用户能在系统设置里改。你可以在创建前检查并设置,创建后只能改名称和描述。
发送通知
val notification = NotificationCompat.Builder(context, "message_channel")
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle("张三")
.setContentText("晚上一起吃饭?")
.setPriority(NotificationCompat.PRIORITY_HIGH) // 7.0 及以下兼容
.setAutoCancel(true) // 点击后自动消失
.setContentIntent(pendingIntent)
.build()
NotificationManagerCompat.from(context).notify(1, notification)
notify 的第一个参数是 notification ID,同一个 ID 发通知会更新已有通知而不是新建。
通知样式
Android 提供了几种扩展样式:
BigTextStyle:长文本展开
NotificationCompat.BigTextStyle()
.bigText("这是一段很长的文本内容...")
.setBigContentTitle("标题")
BigPictureStyle:大图通知
NotificationCompat.BigPictureStyle()
.bigPicture(bitmap)
.bigLargeIcon(null) // 展开时隐藏小图标
InboxStyle:多行摘要
NotificationCompat.InboxStyle()
.addLine("张三:晚上吃饭?")
.addLine("李四:明天开会")
.addLine("王五:文档已发")
.setSummaryText("3 条未读")
MessagingStyle:聊天对话
NotificationCompat.MessagingStyle("我")
.addMessage("晚上吃饭?", System.currentTimeMillis(), "张三")
.addMessage("好的", System.currentTimeMillis() - 60000, "我")
MessagingStyle 是 Google 推荐的聊天类 App 通知样式,系统会自动做会话分组和快捷回复。
通知分组
消息类 App 通知多了怎么办?Android 7.0+ 支持通知分组:
// 子通知
val child = NotificationCompat.Builder(context, "message_channel")
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle("张三")
.setContentText("晚上吃饭?")
.setGroup("chat_group")
.build()
// 汇总通知
val summary = NotificationCompat.Builder(context, "message_channel")
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle("2 条新消息")
.setStyle(NotificationCompat.InboxStyle()
.setSummaryText("聊天消息"))
.setGroup("chat_group")
.setGroupSummary(true)
.build()
// 先发子通知,最后发汇总
notificationManager.notify(1001, child)
notificationManager.notify(1000, summary) // 汇总 ID 最小
快捷回复(RemoteInput)
Android 7.0+ 支持直接在通知里输入回复:
val replyLabel = "回复"
val remoteInput = RemoteInput.Builder("key_reply")
.setLabel(replyLabel)
.build()
val replyAction = NotificationCompat.Action.Builder(
R.drawable.ic_reply,
"回复",
replyPendingIntent
).addRemoteInput(remoteInput).build()
NotificationCompat.Builder(context, "message_channel")
.addAction(replyAction)
.build()
接收回复时从 Intent 提取:
val replyText = RemoteInput.getResultsText(intent, "key_reply")
Android 13+ 通知权限
Android 13(API 33)开始,发送通知需要显式申请权限:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(context,
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
}
用户拒绝后你的通知会静默丢弃(不报错,但用户看不到)。所以要做权限检查和引导。
最佳实践总结
Channel 粒度要合理。太细用户管理麻烦,太粗没法分别控制。按功能模块划分比较好。
重要通知用 HIGH,其他用 LOW。别什么都弹横幅,用户嫌烦会直接关掉整个 Channel。
通知 ID 要有策略。聊天类按会话 ID 生成,系统类固定 ID 覆盖更新。
用 MessagingStyle 做聊天通知。系统会自动处理分组和快捷回复,体验一致。
前台 Service 的通知要特殊处理。可以通过
setForegroundServiceBehavior标记,避免在通知栏造成干扰。别忘了通知的目标是让用户知道有事,不是骚扰用户。
小结
通知看起来简单,但 Channel 设计、样式选择、分组策略、权限处理每一步都影响用户体验。核心原则:让用户在需要的时候看到,不需要的时候不打扰。
下一篇聊 Android 主题系统:Material 3、Dynamic Color 与暗色模式适配。