一、公寓楼的公告栏:通知系统的日常
想象安卓系统是一座名为 "安卓大厦" 的豪华公寓,里面住着形形色色的应用 "居民"。每个居民有时需要向住户们发布重要消息 —— 比如新消息提醒、天气预警或音乐播放状态。这时,大厦里的 "公告栏管理员"——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)来制作通知卡片:
-
设计师先检查通知是否需要显示(比如是否被用户静音或屏蔽)
-
然后创建通知的图标和文字视图(createNotificationViews)
-
最后把通知卡片添加到公告栏(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 清除通知:住户的选择
住户可以通过以下方式清除通知:
-
点击通知卡片(如果设置了自动取消)
-
滑动删除某条通知
-
点击 "清除所有" 按钮
-
应用自己调用 cancel () 方法
java
// 应用清除通知
mNotificationManager.cancel(1); // 清除指定通知
mNotificationManager.cancelAll(); // 清除所有通知
五、幕后的通信奥秘:Binder 的魔法
整个通知流程中,最重要的 "魔法" 是大厦的 "内部对讲机"(Binder 机制):
-
应用通过 Binder 告诉 NMS:"我要发通知"
-
NMS 通过 Binder 告诉 SystemUI:"有新通知啦"
-
SystemUI 通过 Binder 告诉应用:"用户点击通知了"
这种跨进程通信就像大厦里的 "传话筒",让各个部分无需见面就能协同工作,保证了通知系统的高效运行。
六、总结:通知系统的工作流程图解
- 应用层:居民(应用)创建密封纸条(PendingIntent),交给前台接待员(NotificationManager)
- 服务层:公告栏管理员(NMS)接收纸条,分类处理后存入公告列表
- 通信层:通过 Binder 机制通知公告栏(SystemUI)有新公告
- 显示层:公告栏管理员安排设计师制作通知卡片,更新到公告栏
- 互动层:住户点击通知,执行纸条里的指令,通知可能自动消失
七、常见场景:通知的实际应用
-
聊天应用:收到新消息时显示通知,点击后打开聊天页面
-
音乐应用:前台服务通知显示播放状态,支持控制按钮
-
天气应用:定时推送天气预警,点击后查看详情
-
下载服务:后台下载时显示进度条通知,完成后提醒
通过这个故事,我们了解到 NotificationManagerService 就像一位高效的公告栏管理员,不仅负责接收和处理所有通知请求,还通过与公告栏的密切配合,确保住户能及时看到重要信息,同时提供灵活的互动方式,是安卓系统中连接应用和用户的重要桥梁。