安卓通知的奇幻之旅:NotificationManagerService 的故事

74 阅读6分钟

一、公寓楼的公告栏:通知系统的日常

想象安卓系统是一座名为 "安卓大厦" 的豪华公寓,里面住着形形色色的应用 "居民"。每个居民有时需要向住户们发布重要消息 —— 比如新消息提醒、天气预警或音乐播放状态。这时,大厦里的 "公告栏管理员"——NotificationManagerService(简称 NMS)就开始忙碌起来。

1.1 公告栏管理员上岗

每天清晨,当大厦的总管(SystemServer)醒来后,会逐一叫醒各个服务。当叫到公告栏管理员时:

java

// 大厦总管启动通知服务
private void startOtherServices() {
    mSystemServiceManager.startService(NotificationManagerService.class);
}

这位管理员一上岗,就会在大厦的 "中央办公室"(system_server 进程主线程)设立办公桌,并雇佣一批 "文书助理"(Handler)来处理所有通知事务。

1.2 居民的通知纸条:PendingIntent

公寓里的居民(应用程序)想发布通知时,不能直接大喊大叫,而是要写一张 "密封纸条"(PendingIntent)。这张纸条里写着:"当住户看到这条通知时,请帮我做这件事"—— 可以是打开某个页面、启动后台服务或发送广播。

比如,一个聊天应用想提醒用户有新消息,它会写这样一张纸条:

java

// 创建密封纸条:点击通知后打开聊天页面
PendingIntent targetIntent = PendingIntent.getActivity(
    this, 0, new Intent(this, ChatActivity.class), 0);

这张纸条的神奇之处在于,它不仅记录了要做的事情,还自带 "魔法印章"(PendingIntent),确保只有管理员才能打开并执行里面的指令。

二、提交通知:从居民到管理员的传递

2.1 递交通知纸条

居民把密封纸条交给大厦的 "前台接待员"(NotificationManager),接待员会给纸条贴上 "房号标签"(包名和用户 ID),然后通过大厦的 "内部对讲机"(Binder 机制)传给公告栏管理员:

java

// 前台接待员将通知交给管理员
NotificationManager mNotificationManager = 
(NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
mNotificationManager.notify(1, notification);

2.2 管理员的分类处理

公告栏管理员(NMS)收到纸条后,会先检查是否来自合法居民(验证包名和 UID),然后把通知信息封装成 "正式公告单"(StatusBarNotification),并创建 "公告记录"(NotificationRecord):

java

// 管理员创建公告记录
final StatusBarNotification n = new StatusBarNotification(...);
final NotificationRecord r = new NotificationRecord(getContext(), n);

接着,管理员把这份记录交给 "文书助理"(WorkerHandler)在后台处理,避免前台工作被打断:

java

// 交给文书助理异步处理
mHandler.post(new EnqueueNotificationRunnable(userId, r));

2.3 文书助理的工作流程

文书助理拿到公告记录后,会先查看 "公告栏列表"(mNotificationList)是否已有相同的通知:

  • 如果是新通知,就添加到列表

  • 如果是更新通知,就替换旧记录

然后,助理会给重要通知贴上 "特别标签":

  • 如果是前台服务通知(如音乐播放),就加上 "请勿清除" 和 "正在运行" 的标签

  • 最后,整理所有通知的显示顺序

java

// 文书助理处理通知
if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
    notification.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
}
mRankingHelper.sort(mNotificationList); // 排序通知

三、公告栏的显示:从管理员到住户的传递

3.1 通知监听器:公告栏的 "眼线"

大厦的 "公告栏"(SystemUI)有自己的管理员,他会提前向通知管理员注册成为 "监听器",就像订阅了一份 "通知报纸":

java

// 公告栏管理员注册监听器
mNotificationListener.registerAsSystemService(mContext, componentName, UserHandle.USER_ALL);

当通知管理员有新通知时,就会通过 "内部对讲机" 告诉公告栏管理员:"有新公告啦!"

3.2 公告栏的更新流程

公告栏管理员收到通知后,会安排 "版面设计师"(Handler)来制作通知卡片:

  1. 设计师先检查通知是否需要显示(比如是否被用户静音或屏蔽)

  2. 然后创建通知的图标和文字视图(createNotificationViews)

  3. 最后把通知卡片添加到公告栏(addNotificationViews)

java

// 制作通知视图并添加到公告栏
protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {
    final StatusBarIconView iconView = createIcon(sbn); // 创建图标
    NotificationData.Entry entry = new NotificationData.Entry(sbn, iconView);
    inflateViews(entry, mStackScroller); // 填充视图内容
    return entry;
}

3.3 住户的互动:点击通知

当住户点击公告栏上的通知卡片时,公告栏管理员会拆开当初的 "密封纸条"(PendingIntent),执行里面的指令:

  • 如果是打开页面,就像打开住户的房门(启动 Activity)

  • 如果是后台服务,就像派工作人员去处理(启动 Service)

  • 如果设置了自动取消,通知卡片会像魔法一样消失(FLAG_AUTO_CANCEL)

java

// 点击通知后执行纸条里的指令
notification.operation.send(...); // 执行PendingIntent中的操作

四、管理员的特殊职责:前台服务通知

4.1 重要公告:前台服务的 "特权"

有些服务就像大厦里的 "24 小时便利店"(前台服务),需要一直显示通知让住户知道其存在。比如音乐播放服务,它的通知会有特殊 "特权":

  • 不能被轻易清除(FLAG_NO_CLEAR)

  • 会一直显示在公告栏(FLAG_ONGOING_EVENT)

  • 即使大厦进入 "节能模式" 也不会消失

java

// 前台服务设置通知
startForeground(1, notification); // 启动前台服务并显示通知

4.2 清除通知:住户的选择

住户可以通过以下方式清除通知:

  1. 点击通知卡片(如果设置了自动取消)

  2. 滑动删除某条通知

  3. 点击 "清除所有" 按钮

  4. 应用自己调用 cancel () 方法

java

// 应用清除通知
mNotificationManager.cancel(1); // 清除指定通知
mNotificationManager.cancelAll(); // 清除所有通知

五、幕后的通信奥秘:Binder 的魔法

整个通知流程中,最重要的 "魔法" 是大厦的 "内部对讲机"(Binder 机制):

  • 应用通过 Binder 告诉 NMS:"我要发通知"

  • NMS 通过 Binder 告诉 SystemUI:"有新通知啦"

  • SystemUI 通过 Binder 告诉应用:"用户点击通知了"

这种跨进程通信就像大厦里的 "传话筒",让各个部分无需见面就能协同工作,保证了通知系统的高效运行。

六、总结:通知系统的工作流程图解

  1. 应用层:居民(应用)创建密封纸条(PendingIntent),交给前台接待员(NotificationManager)
  2. 服务层:公告栏管理员(NMS)接收纸条,分类处理后存入公告列表
  3. 通信层:通过 Binder 机制通知公告栏(SystemUI)有新公告
  4. 显示层:公告栏管理员安排设计师制作通知卡片,更新到公告栏
  5. 互动层:住户点击通知,执行纸条里的指令,通知可能自动消失

七、常见场景:通知的实际应用

  • 聊天应用:收到新消息时显示通知,点击后打开聊天页面

  • 音乐应用:前台服务通知显示播放状态,支持控制按钮

  • 天气应用:定时推送天气预警,点击后查看详情

  • 下载服务:后台下载时显示进度条通知,完成后提醒

通过这个故事,我们了解到 NotificationManagerService 就像一位高效的公告栏管理员,不仅负责接收和处理所有通知请求,还通过与公告栏的密切配合,确保住户能及时看到重要信息,同时提供灵活的互动方式,是安卓系统中连接应用和用户的重要桥梁。