NotificationManagerService
intro
在 Android 系統中,通知 (Notification) 是應用程式與使用者溝通最重要的方式之一。
它提供了一種非侵入性的互動方式,可以讓應用程式在不打斷使用者操作的前提下,及時地傳遞訊息。
而背後的關鍵角色,就是 NotificationManagerService (NMS)。
NMS 的角色可以簡單理解為:
- 應用程式層 → 呼叫 NotificationManager 發送通知
- 系統服務層 → NotificationManagerService 接收、管理、排序與分發通知
- UI 呈現層 → 狀態列 (StatusBar)、通知欄 (Notification Shade) 負責渲染
通知系統整體架構
Android 的通知系統由三個主要部分組成:
- 應用程式 (App Layer)
- 開發者透過 NotificationManager API 發送通知
- 使用 NotificationChannel 來設定通知層級、聲音、震動等行為
- 系統服務 (System Server)
- NotificationManagerService (NMS)
- 負責接收與管理所有 App 的通知
- 處理通知的新增、更新、移除、排序
- 檢查權限與通知通道設定
- 系統 UI (SystemUI Process)
- StatusBarManagerService (SBMS) 與 SystemUI 協作
- 負責顯示通知在狀態列、下拉通知欄
- 使用 StatusBar 介面與 NMS 交互
[App]
│ (NotificationManager)
▼
[Binder IPC]
│
▼
[System Server]
└─ NotificationManagerService
│
├─ 檢查權限、通道、排序
│
└─ StatusBarManagerService
│
▼
[SystemUI Process]
│
▼
狀態列 (StatusBar) / 通知欄 (Shade)
NotificationManagerService 的啟動與註冊
在 SystemServer 啟動流程中,會建立 NotificationManagerService:
NotificationManagerService nms = new NotificationManagerService(context);
ServiceManager.addService(Context.NOTIFICATION_SERVICE, nms);
流程:
- 初始化 NMS:建立內部資料結構 (例如 NotificationRecord 列表)
- 註冊至 ServiceManager:讓應用程式透過 Binder 呼叫
3.與 StatusBarManagerService 建立連結:通知能夠顯示在狀態列
通知的生命週期
- 通知的發送
應用程式端:
NotificationManager nm = context.getSystemService(NotificationManager.class);
Notification notification = new NotificationCompat.Builder(context, "channel_id")
.setContentTitle("訊息提醒")
.setContentText("您有一則新訊息")
.setSmallIcon(R.drawable.ic_notify)
.build();
nm.notify(1, notification);
實際流程:
- App 呼叫 NotificationManager.notify()
- Binder IPC → NotificationManagerService.enqueueNotificationInternal()
- NMS 檢查:
- 應用是否有通知權限
- 通知通道 (NotificationChannel) 是否存在
- 系統設定是否允許顯示
-
NMS 處理邏輯
NMS 收到通知後,會建立 NotificationRecord,並放入通知列表中。
主要流程:
-
建立 NotificationRecord
2.檢查 優先級 (Priority) 與 重要性 (Importance)
-
檢查 鎖定畫面 (LockScreen Visibility)
-
決定是否需要提醒 (聲音 / 震動 / Heads-up)
-
通知 StatusBarManagerService 更新 UI
-
通知的顯示
- StatusBarManagerService (SBMS) 會透過 IStatusBar 介面,把通知傳遞給 SystemUI Process
- SystemUI 中的 NotificationShadeWindow / StatusBar 負責渲染 UI
- 狀態列會顯示小圖示,下拉後顯示完整通知卡片
- 通知的更新
當應用程式再次呼叫 notify(),如果通知 ID 相同:
- NMS 會將舊的 NotificationRecord 替換
- StatusBar UI 重新刷新該通知
- 通知的移除
應用程式呼叫:
nm.cancel(1);
流程:
- Binder IPC → NotificationManagerService.cancelNotification()
- NMS 從通知列表移除
- 通知 StatusBarManagerService 更新畫面
通知通道 (NotificationChannel)
從 Android 8.0 (Oreo) 開始,所有通知必須綁定 NotificationChannel。
- 為何需要 NotificationChannel?
- 提供使用者細粒度控制
- 不同類型通知可以有不同的重要性
- 使用者可透過系統設定介面管理
- 通道的重要性等級
|等級 | 常數 | 行為
|----|----|----|
|高 |IMPORTANCE_HIGH |Heads-up 通知、音效、震動
|中 |IMPORTANCE_DEFAULT |顯示在狀態列,有聲音
|低 |IMPORTANCE_LOW |顯示在狀態列,無聲音
|最低 |IMPORTANCE_MIN |不顯示在狀態列,只在下拉欄中出現
- 流程
- App 建立 Channel → 註冊給 NMS
- 使用者可在設定中修改 Channel 行為
- NMS 會根據 Channel 設定,決定通知的最終表現
狀態列事件 (StatusBar Events)
NMS 不僅負責通知,還需要與 StatusBar 協作,處理「狀態列事件」。
- 狀態列小圖示 (StatusBar Icon)
- 每個通知可指定 setSmallIcon()
- NMS 通知 SBMS → SBMS 更新 SystemUI → 狀態列顯示圖示
- Heads-up 通知
- 當通知重要性高 (High) 且滿足條件,會觸發 Heads-up 彈出視窗
- SystemUI 負責渲染浮動通知卡片
- 鎖定畫面通知
- NMS 檢查通知可見性 (VISIBILITY_PUBLIC / PRIVATE / SECRET)
- 決定是否在鎖屏畫面顯示
App.notify()
│
▼
NotificationManagerService
│
▼
StatusBarManagerService
│
▼
SystemUI(StatusBar/NotificationShade)
│
▼
狀態列圖示 / Heads-up / 通知卡片
權限與安全性
- 通知權限
- Android 13 起,需要 POST_NOTIFICATIONS 權限
-NMS 會檢查是否授權
- 特殊通知
- 系統通知 (foreground service、重要警告) 可能有額外保護
- 某些通知只能由系統應用建立 (例如藍牙、網路狀態)
- 使用者控制
- 使用者可關閉 App 的所有通知
- 或僅關閉某些 Channel
Summary
NotificationManagerService (NMS) 是 Android 通知系統的核心:
- 負責接收、管理、分發通知
- 與 StatusBarManagerService、SystemUI 協作,完成通知的 UI 呈現
- 通知通道 (NotificationChannel) 提供細粒度控制
- 狀態列事件 (小圖示、Heads-up、鎖屏通知) 都由 NMS 間接驅動