第4章:Notification 构建与渠道适配
自 Android 8.0(API level 26)起,所有通知必须关联一个通知渠道(NotificationChannel),否则通知将不会显示。前台服务作为长期运行且必须绑定通知的场景,正确创建渠道并构建通知是兼容多版本的基础。本章详细讲解渠道创建、通知构建及版本兼容的最佳实践。
4.1 NotificationChannel 的创建
每个渠道由唯一渠道 ID、用户可见名称、重要等级构成。重要等级直接影响通知的展示方式(弹窗、音效、是否显示)。建议在 Application.onCreate() 或首次显示通知前统一创建渠道,避免重复创建。
| 参数 | 说明 | 建议值 |
|---|---|---|
| channelId | 渠道唯一标识,必须与通知绑定 | "foreground_service" |
| channelName | 用户在系统设置中看到的名称 | "定位服务" 或 "后台任务" |
| importance | 重要等级 (IMPORTANCE_LOW / DEFAULT / HIGH) | 前台服务通常使用 IMPORTANCE_LOW 或 DEFAULT |
核心代码:创建并注册通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
"前台服务通知",
NotificationManager.IMPORTANCE_LOW
);
channel.setDescription("用于显示前台服务持续运行的通知");
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
}
注意:createNotificationChannel() 对相同 channelId 的调用是幂等的,可安全重复执行。若用户手动修改渠道设置,后续代码不会覆盖用户偏好。
4.2 使用 NotificationCompat 构建通知
为了兼容 Android 8.0 以下版本,必须使用 NotificationCompat.Builder 而非 Notification.Builder。其内部自动处理渠道绑定逻辑:
- 在 API 26+ 上,
setChannelId()被强制性使用。 - 在 API 25- 上,
setChannelId()无效果,但仍推荐调用,确保一份代码兼容所有版本。
兼容性要点:
NotificationCompat.Builder 会自动忽略不支持的属性,不会崩溃。但缺少渠道 ID 的通知在 Oreo 以上设备会被静默丢弃。
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("前台服务运行中")
.setContentText("正在获取位置更新")
.setSmallIcon(R.drawable.ic_notification)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setOngoing(true) // 用户无法滑动清除
.build();
4.3 版本适配与降级策略
当 targetSdkVersion < 26 时,系统不会强制要求渠道,但建议统一升级。下表总结不同 SDK 版本下的行为:
| 运行环境 | 渠道是否必须 | 构建方式 | 注意事项 |
|---|---|---|---|
| API 26+,target ≥ 26 | 是 | NotificationCompat.Builder + channelId | 渠道必须在通知前创建 |
| API 26+,target < 26 | 否(系统自动分配渠道) | NotificationCompat.Builder | 推荐主动创建以便用户管理 |
| API 25- | 否 | NotificationCompat.Builder | 无需渠道,正常显示 |
常见陷阱: 在 API 26+ 上使用
Notification.Builder(非 Compat)且未设置 channelId,前台服务会启动失败并抛出 RuntimeException: Bad notification for startForeground。务必使用 NotificationCompat.Builder。
4.4 启动前台服务前构建通知的完整流程
以下整合了渠道创建、通知构建、服务启动的典型代码(位于 onStartCommand 或 onCreate):
private void startForegroundService() {
// 1. 创建渠道(仅在 API 26+ 需要)
createNotificationChannel();
// 2. 构建通知
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("正在执行同步任务")
.setContentText("进度:50%")
.setSmallIcon(R.drawable.ic_sync)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setOngoing(true)
.build();
// 3. 启动前台服务
startForeground(NOTIFICATION_ID, notification);
}
@RequiresApi(api = Build.VERSION_CODES.O)
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
"同步服务",
NotificationManager.IMPORTANCE_DEFAULT
);
channel.setDescription("用于显示后台同步的状态");
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
}
}
4.5 渠道与通知的生命周期关系图
SVG 说明: 以下流程图展示从渠道创建到前台通知显示的完整链路,凸显 Android 8.0+ 中渠道作为前置条件的约束。
4.6 调试与验证
可以通过以下方式确认通知渠道是否生效:
- 打开系统设置 → 应用 → 通知 → 找到你的应用,查看渠道列表。
- 使用 adb 命令检查渠道信息:
adb shell dumpsys notification --package com.example.yourapp
# 输出中会列出所有注册的渠道及重要等级
若前台服务通知不显示,优先检查:
- 渠道是否已创建(查看 logcat 中是否有
No channel found异常)。 NotificationCompat.Builder构造函数的第二个参数是否与渠道 ID 一致。startForeground()的NOTIFICATION_ID是否为正数。
4.7 最佳实践总结
| 最佳实践 | 说明 |
|---|---|
| 渠道创建时机 | 在 Application.onCreate() 或第一次启动前台服务前 |
| 重要等级选择 | 前台服务如为持续定位,建议 IMPORTANCE_LOW 以减少扰民 |
| 渠道 ID 常量 | 使用独立常量避免拼写错误 |
| 使用 NotificationCompat | 始终使用 support library 版本,确保向后兼容 |
| 测试多版本 | 在 API 26 模拟器和真实设备上验证通知显示 |
本课小结: 正确创建 NotificationChannel 并配合 NotificationCompat 构建前台服务通知,是跨版本兼容的基石。忽视渠道将直接导致前台服务在 Android 8.0+ 上无法启动。下一章将深入探讨前台服务的生命周期回调与启动/停止的异常场景。
第4章 结束