通知系统
本教程共 100 篇 · 第 84 篇 · 更新于 2026-07-28 · 约 8 分钟阅读
84. 通知系统
本节目标:学会创建通知渠道、用 NotificationCompat.Builder 构建基本通知、设置点击跳转、添加操作按钮,理解 Android 13+ 的通知权限。
通知是应用在不在前台时给用户传递信息的渠道。新消息、闹钟、下载完成,都靠通知。这章讲基础,下一章讲进阶样式。
通知渠道(Channel)
Android 8.0(API 26)起,所有通知必须挂在一个「渠道」上。没有渠道的通知直接不显示。
打个比方,渠道像新闻订阅的分类—「新闻」「体育」「娱乐」各一个分类,用户能单独开关每个分类的通知,单独设铃声、震动。
创建渠道:
private const val CHANNEL_ID = "messages"
fun createChannel(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = "消息通知"
val description = "新消息到达时通知"
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
this.description = description
}
val manager = context.getSystemService(NotificationManager::class.java)
manager.createNotificationChannel(channel)
}
}
要点:
CHANNEL_ID是渠道唯一标识,发通知时用。importance决定打扰程度:MIN(不响)、LOW(小通知)、DEFAULT(响铃)、HIGH(弹出)、MAX(紧急)。- 渠道一旦创建,重要性就不能改了(用户能在设置里改)。
- 应用启动时调用即可,重复创建无害。
Note多个渠道按用途分:聊天、下载、营销等。用户能精细控制「我不想收到营销通知但保留聊天」。
通知权限(Android 13+)
Android 13(API 33)起,发通知要先申请运行时权限:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
请求时机:应用首次需要发通知时。一般是在主 Activity 启动时弹一次,但更推荐「用户主动触发」时弹。
val launcher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
if (granted) {
// 可以发通知
}
}
// 请求
launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
WarningtargetSdk >= 33 但没申请权限,所有通知都不显示。老应用升级时别漏。
构建基本通知
用 NotificationCompat.Builder:
fun sendBasicNotification(context: Context) {
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification) // 必填
.setContentTitle("新消息")
.setContentText("你有一封来自张三的邮件")
.setPriority(NotificationCompat.PRIORITY_DEFAULT) // < API 26 用
.setAutoCancel(true) // 点击后自动消失
NotificationManagerCompat.from(context).apply {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS)
== PackageManager.PERMISSION_GRANTED || Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
notify(NOTIFICATION_ID, builder.build())
}
}
}
要点:
setSmallIcon必填,没这个通知不显示。用单色透明 PNG,系统自动着色。setContentTitle/setContentText标题和正文。setPriority是 API 26 以下用的优先级,26 以上看渠道重要性。setAutoCancel(true)点击后自动消失。notify(id, notification)发出,id 用来后续更新或取消。
点击跳转
通知默认点击没反应。要让它打开 Activity,设置 PendingIntent:
val intent = Intent(context, DetailActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
putExtra("message_id", 123)
}
val pendingIntent = PendingIntent.getActivity(
context,
0,
intent,
PendingIntent.FLAG_IMMUTABLE // Android 12+ 必须指定
)
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle("新消息")
.setContentText("点开查看")
.setContentIntent(pendingIntent) // 点击事件
.setAutoCancel(true)
WarningAndroid 12(API 31)起,
PendingIntent必须显式指定FLAG_IMMUTABLE或FLAG_MUTABLE,不写会崩。除非要往 PendingIntent 里塞数据(如直接回复),都用FLAG_IMMUTABLE。
操作按钮
通知能加最多 3 个操作按钮:
val replyIntent = Intent(context, ReplyReceiver::class.java).apply {
action = "ACTION_REPLY"
putExtra("message_id", 123)
}
val replyPendingIntent = PendingIntent.getBroadcast(
context, 0, replyIntent, PendingIntent.FLAG_IMMUTABLE
)
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle("新消息")
.setContentText("...")
.addAction(R.drawable.ic_reply, "回复", replyPendingIntent)
.addAction(R.drawable.ic_archive, "归档", archivePendingIntent)
addAction 第一个参数是图标,第二个是文字,第三个是 PendingIntent。点击后启动对应的 Intent(一般是 BroadcastReceiver)。
Note操作按钮不要和点击主通知的动作重复。比如点通知本身打开详情页,按钮就放「回复」「归档」这种快捷操作。
更新通知
发新通知时用同一个 id,会更新原通知:
// 第一次
notify(100, builder.build())
// 更新进度
builder.setContentText("进度 50%")
notify(100, builder.build())
加 setOnlyAlertOnce(true),只在第一次响,更新时不响。
取消通知
NotificationManagerCompat.from(context).cancel(notificationId) // 取消指定
NotificationManagerCompat.from(context).cancelAll() // 取消所有
或者用 setTimeoutAfter(5000),5 秒后自动消失。
通知优先级
API 26 以下用 setPriority:
PRIORITY_MIN:无打扰,状态栏一行。PRIORITY_LOW:无声音,下拉才看到。PRIORITY_DEFAULT:默认响铃。PRIORITY_HIGH:弹出通知栏。PRIORITY_MAX:全屏弹出。
API 26+ 用渠道重要性:
IMPORTANCE_MIN/IMPORTANCE_LOW/IMPORTANCE_DEFAULT/IMPORTANCE_HIGH
Tip优先级只是建议,系统会根据用户行为调整。比如用户经常忽略某个高优先级通知,系统会降级。
锁屏可见性
控制锁屏时通知显示多少内容:
.setVisibility(NotificationCompat.VISIBILITY_PRIVATE)
三档:
VISIBILITY_PUBLIC:完整显示。VISIBILITY_PRIVATE:只显示「应用名 - 1 条通知」。VISIBILITY_SECRET:完全不显示。
VISIBILITY_PRIVATE 还能配一个备用通知:
val publicVersion = NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle("新消息")
.setContentText("你有一条新消息")
.build()
builder.setPublicVersion(publicVersion)
前台服务通知
前台服务必须挂通知,告诉用户「应用正在后台做事」:
class MusicService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val notification = NotificationCompat.Builder(this, "music")
.setContentTitle("正在播放")
.setContentText("歌曲名")
.setSmallIcon(R.drawable.ic_music)
.build()
startForeground(NOTIFICATION_ID, notification)
return START_STICKY
}
}
WarningAndroid 14+ 前台服务要声明
foregroundServiceType(如mediaPlayback),并申请对应权限。漏一个崩。
Compose 里发通知
Compose 里发通知和传统方式一样,因为通知是系统级的,和 UI 框架无关。在 Composable 里用 LocalContext 拿 Context:
@Composable
fun NotificationButton() {
val context = LocalContext.current
Button(onClick = {
createChannel(context)
sendBasicNotification(context)
}) {
Text("发通知")
}
}
常见坑
- API 26+ 没建渠道:通知不显示。
- 小图标用彩色 PNG:状态栏显示一坨,用单色透明图。
- PendingIntent 没指定 MUTABLE/IMMUTABLE:Android 12+ 崩。
- targetSdk 33+ 没申请通知权限:通知全部不显示。
- 同 id 通知被覆盖:以为没发出去,其实是更新了。
- 渠道重要性创建后改不了:想升级得新建渠道(旧渠道让用户在设置里禁用)。
小结
通知系统三件套:渠道(API 26+)、Builder、notify。先建渠道,再 Builder 构建通知,最后 NotificationManagerCompat.notify() 发出。点击跳转用 setContentIntent + PendingIntent,操作按钮用 addAction。Android 13+ 要申请 POST_NOTIFICATIONS 权限。
下一章讲通知进阶,看大图、分组、进度这些样式。