Administrator
发布于 2026-08-25 / 19 阅读
0
0

系列文章《Android 工程师的 100 篇进阶笔记》第 34 篇:Android 通知体系:Channels、Styles 与最佳实践

上一篇讲了进程生命周期,这篇聊一个开发者天天碰但很少深入的主题——通知。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 级别:

级别

效果

HIGH

弹出横幅 + 声音 + 振动

DEFAULT

声音 + 振动,无横幅

LOW

状态栏显示,无声无振动

MIN

几乎不可见

重要一点: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)
    }
}

用户拒绝后你的通知会静默丢弃(不报错,但用户看不到)。所以要做权限检查和引导。

最佳实践总结

  1. Channel 粒度要合理。太细用户管理麻烦,太粗没法分别控制。按功能模块划分比较好。

  2. 重要通知用 HIGH,其他用 LOW。别什么都弹横幅,用户嫌烦会直接关掉整个 Channel。

  3. 通知 ID 要有策略。聊天类按会话 ID 生成,系统类固定 ID 覆盖更新。

  4. 用 MessagingStyle 做聊天通知。系统会自动处理分组和快捷回复,体验一致。

  5. 前台 Service 的通知要特殊处理。可以通过 setForegroundServiceBehavior 标记,避免在通知栏造成干扰。

  6. 别忘了通知的目标是让用户知道有事,不是骚扰用户。

小结

通知看起来简单,但 Channel 设计、样式选择、分组策略、权限处理每一步都影响用户体验。核心原则:让用户在需要的时候看到,不需要的时候不打扰。

下一篇聊 Android 主题系统:Material 3、Dynamic Color 与暗色模式适配。


评论