如7.1.1節所述,狀態欄與導航欄的啟動由其PhoneStatusBar.start()完成。參考其實現:
**PhoneStatusBar.java-->PhoneStatusBar.start()**
```
public void start() {
......
// **① 調用父類BaseStatusBar的start()方法進行初始化。**
super.start();
// 創建導航欄的窗口
addNavigationBar();
// **② 創建PhoneStatusBarPolicy。**PhoneStatusBarPolicy定義了系統通知圖標的設置策略
mIconPolicy = new PhoneStatusBarPolicy(mContext);
}
```
參考BaseStatusBar.start()的實現,這段代碼比較長,并且涉及到了本章隨后會詳細介紹的內容。因此倘若讀者閱讀起來比較吃力可以僅關注那三個關鍵步驟。在完成本章的學習之后再回過頭來閱讀這部分代碼便會發現十分簡單了。
**BaseStatusBar-->BaseStatusBar.start()**
```
public void start() {
/* 由于狀態欄的窗口不屬于任何一個Activity,所以需要使用第6章所介紹的WindowManager
進行窗口的創建 */
mWindowManager = (WindowManager)mContext
.getSystemService(Context.WINDOW_SERVICE);
/* 在第4章介紹窗口的布局時曾經提到狀態欄的存在對窗口布局有著重要的影響。因此狀態欄中
所發生的變化有必要通知給WMS */
mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
......
/*mProvisioningOberver是一個ContentObserver。
它負責監聽Settings.Global.DEVICE_PROVISIONED設置的變化。這一設置表示此設備是否已經
歸屬于某一個用戶。比如當用戶打開一個新購買的設備時,初始化設置向導將會引導用戶閱讀使用條款、
設置帳戶等一系列的初始化操作。在初始化設置向導完成之前,
Settings.Global.DEVICE_PROVISIONED的值為false,表示這臺設備并未歸屬于某
一個用戶。
當設備并未歸屬于某以用戶時,狀態欄會禁用一些功能以避免信息的泄露。mProvisioningObserver
即是用來監聽設備歸屬狀態的變化,以禁用或啟用某些功能 */
mProvisioningObserver.onChange(false); // set up
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
mProvisioningObserver);
/* **① 獲取IStatusBarService的實例。**IStatusBarService是一個系統服務,由ServerThread
啟動并常駐system_server進程中。IStatusBarService為那些對狀態欄感興趣的其他系統服務定
義了一系列API,然而對SystemUI而言,它更像是一個客戶端。因為IStatusBarService會將操作
狀態欄的請求發送給SystemUI,并由后者完成請求 */
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
/* 隨后BaseStatusBar將自己注冊到IStatusBarService之中。以此聲明本實例才是狀態欄的真正
實現者,IStatusBarService會將其所接受到的請求轉發給本實例。
“天有不測風云”,SystemUI難免會因為某些原因使得其意外終止。而狀態欄中所顯示的信息并不屬于狀態
欄自己,而是屬于其他的應用程序或是其他的系統服務。因此當SystemUI重新啟動時,便需要恢復其
終止前所顯示的信息以避免信息的丟失。為此,IStatusBarService中保存了所有的需要狀態欄進行顯
示的信息的副本,并在新的狀態欄實例啟動后,這些副本將會伴隨著注冊的過程傳遞給狀態欄并進行顯示,
從而避免了信息的丟失。
從代碼分析的角度來看,這一從IstatusBarService中取回信息副本的過程正好完整地體現了狀態欄
所能顯示的信息的類型*/
/*iconList是向IStatusBarService進行注冊的參數之一。它保存了用于顯示在狀態欄的系統狀態
區中的狀態圖標列表。在完成注冊之后,IStatusBarService將會在其中填充兩個數組,一個字符串
數組用于表示狀態的名稱,一個StatusBarIcon類型的數組用于存儲需要顯示的圖標資源。
關于系統狀態區的工作原理將在7.2.3節介紹*/
StatusBarIconList iconList = new StatusBarIconList();
/*notificationKeys和StatusBarNotification則存儲了需要顯示在狀態欄的通知區中通知信息。
前者存儲了一個用Binder表示的通知發送者的ID列表。而notifications則存儲了通知列表。二者
通過索引號一一對應。關于通知的工作原理將在7.2.2節介紹 */
ArrayList<IBinder> notificationKeys = newArrayList<IBinder>();
ArrayList<StatusBarNotification> notifications
= newArrayList<StatusBarNotification>();
/*mCommandQueue是CommandQueue類的一個實例。CommandQueue繼承自IStatusBar.Stub。
因此它是IStatusBar的Bn端。在完成注冊后,這一Binder對象的Bp端將會保存在
IStatusBarService之中。因此它是IStatusBarService與BaseStatusBar進行通信的橋梁。
*/
mCommandQueue= new CommandQueue(this, iconList);
/*switches則存儲了一些雜項:禁用功能列表,SystemUIVisiblity,是否在導航欄中顯示虛擬的
菜單鍵,輸入法窗口是否可見、輸入法窗口是否消費BACK鍵、是否接入了實體鍵盤、實體鍵盤是否被啟用。
在后文中將會介紹它們的具體影響 */
int[]switches = new int[7];
ArrayList<IBinder> binders = new ArrayList<IBinder>();
try {
// **② 向IStatusBarServie進行注冊,并獲取所有保存在IStatusBarService中的信息副本**
mBarService.registerStatusBar(mCommandQueue, iconList,
notificationKeys,notifications,
switches, binders);
} catch(RemoteException ex) {......}
// **③ 創建狀態欄與導航欄的窗口。**由于創建狀態欄與導航欄的窗口涉及到控件樹的創建,因此它由子類
PhoneStatusBar或TabletStatusBar實現,以根據不同的布局方案選擇創建不同的窗口與控件樹 */
createAndAddWindows();
/*應用來自IStatusBarService中所獲取的信息
mCommandQueue已經注冊到IStatusBarService中,狀態欄與導航欄的窗口與控件樹也都創建完畢
因此接下來的任務就是應用從IStatusBarService中所獲取的信息 */
disable(switches[0]); // 禁用某些功能
setSystemUiVisibility(switches[1], 0xffffffff); // 設置SystemUIVisibility
topAppWindowChanged(switches[2]!= 0); // 設置菜單鍵的可見性
// 根據輸入法窗口的可見性調整導航欄的樣式
setImeWindowStatus(binders.get(0), switches[3], switches[4]);
// 設置硬件鍵盤信息。
setHardKeyboardStatus(switches[5] != 0, switches[6] != 0);
// 依次向系統狀態區添加狀態圖標
int N = iconList.size();
......
// 依次向通知欄添加通知
N = notificationKeys.size();
......
/* 至此,與IStatusBarService的連接已建立,狀態欄與導航欄的窗口也已完成創建與顯示,并且
保存在IStatusBarService中的信息都已完成了顯示或設置。狀態欄與導航欄的啟動正式完成 */
}
```
可見,狀態欄與導航欄的啟動分為如下幾個過程:
- 獲取IStatusBarService,IStatusBarService是運行于system\_server的一個系統服務,它接受操作狀態欄/導航欄的請求并將其轉發給BaseStatusBar。為了保證SystemUI意外退出后不會發生信息丟失,IStatusBarService保存了所有需要狀態欄與導航欄進行顯示或處理的信息副本。
- 將一個繼承自IStatusBar.Stub的CommandQueue的實例注冊到IStatusBarService以建立通信,并將信息副本取回。
- 通過調用子類的createAndAddWindows()方法完成狀態欄與導航欄的控件樹及窗口的創建與顯示。
- 使用從IStatusBarService取回的信息副本。
- 前言
- 推薦序
- 第1章 開發環境部署
- 1.1獲取Android源代碼
- 1.2Android的編譯
- 1.3在IDE中導入Android源代碼
- 1.3.1將Android源代碼導入Eclipse
- 1.3.2將Android源代碼導入SourceInsight
- 1.4調試Android源代碼
- 1.4.1使用Eclipse調試Android Java源代碼
- 1.4.2使用gdb調試Android C/C 源代碼
- 1.5本章小結
- 第2章 深入理解Java Binder和MessageQueue
- 2.1概述
- 2.2Java層中的Binder分析
- 2.2.1Binder架構總覽
- 2.2.2初始化Java層Binder框架
- 2.2.3窺一斑,可見全豹乎
- 2.2.4理解AIDL
- 2.2.5Java層Binder架構總結
- 2.3心系兩界的MessageQueue
- 2.3.1MessageQueue的創建
- 2.3.2提取消息
- 2.3.3nativePollOnce函數分析
- 2.3.4MessageQueue總結
- 2.4本章小結
- 第3章 深入理解AudioService
- 3.1概述
- 3.2音量管理
- 3.2.1音量鍵的處理流程
- 3.2.2通用的音量設置函數setStreamVolume()
- 3.2.3靜音控制
- 3.2.4音量控制小結
- 3.3音頻外設的管理
- 3.3.1 WiredAccessoryObserver 設備狀態的監控
- 3.3.2AudioService的外設狀態管理
- 3.3.3音頻外設管理小結
- 3.4AudioFocus機制的實現
- 3.4.1AudioFocus簡單的例子
- 3.4.2AudioFocus實現原理簡介
- 3.4.3申請AudioFocus
- 3.4.4釋放AudioFocus
- 3.4.5AudioFocus小結
- 3.5AudioService的其他功能
- 3.6本章小結
- 第4章 深入理解WindowManager-Service
- 4.1初識WindowManagerService
- 4.1.1一個從命令行啟動的動畫窗口
- 4.1.2WMS的構成
- 4.1.3初識WMS的小結
- 4.2WMS的窗口管理結構
- 4.2.1理解WindowToken
- 4.2.2理解WindowState
- 4.2.3理解DisplayContent
- 4.3理解窗口的顯示次序
- 4.3.1主序、子序和窗口類型
- 4.3.2通過主序與子序確定窗口的次序
- 4.3.3更新顯示次序到Surface
- 4.3.4關于顯示次序的小結
- 4.4窗口的布局
- 4.4.1從relayoutWindow()開始
- 4.4.2布局操作的外圍代碼分析
- 4.4.3初探performLayoutAndPlaceSurfacesLockedInner()
- 4.4.4布局的前期處理
- 4.4.5布局DisplayContent
- 4.4.6布局的階段
- 4.5WMS的動畫系統
- 4.5.1Android動畫原理簡介
- 4.5.2WMS的動畫系統框架
- 4.5.3WindowAnimator分析
- 4.5.4深入理解窗口動畫
- 4.5.5交替運行的布局系統與動畫系統
- 4.5.6動畫系統總結
- 4.6本章小結
- 第5章 深入理解Android輸入系統
- 5.1初識Android輸入系統
- 5.1.1getevent與sendevent工具
- 5.1.2Android輸入系統簡介
- 5.1.3IMS的構成
- 5.2原始事件的讀取與加工
- 5.2.1基礎知識:INotify與Epoll
- 5.2.2 InputReader的總體流程
- 5.2.3 深入理解EventHub
- 5.2.4 深入理解InputReader
- 5.2.5原始事件的讀取與加工總結
- 5.3輸入事件的派發
- 5.3.1通用事件派發流程
- 5.3.2按鍵事件的派發
- 5.3.3DispatcherPolicy與InputFilter
- 5.3.4輸入事件的派發總結
- 5.4輸入事件的發送、接收與反饋
- 5.4.1深入理解InputChannel
- 5.4.2連接InputDispatcher和窗口
- 5.4.3事件的發送
- 5.4.4事件的接收
- 5.4.5事件的反饋與發送循環
- 5.4.6輸入事件的發送、接收與反饋總結
- 5.5關于輸入系統的其他重要話題
- 5.5.1輸入事件ANR的產生
- 5.5.2 焦點窗口的確定
- 5.5.3以軟件方式模擬用戶操作
- 5.6本章小結
- 第6章 深入理解控件系統
- 6.1 初識Android的控件系統
- 6.1.1 另一種創建窗口的方法
- 6.1.2 控件系統的組成
- 6.2 深入理解WindowManager
- 6.2.1 WindowManager的創建與體系結構
- 6.2.2 通過WindowManagerGlobal添加窗口
- 6.2.3 更新窗口的布局
- 6.2.4 刪除窗口
- 6.2.5 WindowManager的總結
- 6.3 深入理解ViewRootImpl
- 6.3.1 ViewRootImpl的創建及其重要的成員
- 6.3.2 控件系統的心跳:performTraversals()
- 6.3.3 ViewRootImpl總結
- 6.4 深入理解控件樹的繪制
- 6.4.1 理解Canvas
- 6.4.2 View.invalidate()與臟區域
- 6.4.3 開始繪制
- 6.4.4 軟件繪制的原理
- 6.4.5 硬件加速繪制的原理
- 6.4.6 使用繪圖緩存
- 6.4.7 控件動畫
- 6.4.8 繪制控件樹的總結
- 6.5 深入理解輸入事件的派發
- 6.5.1 觸摸模式
- 6.5.2 控件焦點
- 6.5.3 輸入事件派發的綜述
- 6.5.4 按鍵事件的派發
- 6.5.5 觸摸事件的派發
- 6.5.6 輸入事件派發的總結
- 6.6 Activity與控件系統
- 6.6.1 理解PhoneWindow
- 6.6.2 Activity窗口的創建與顯示
- 6.7 本章小結
- 第7章 深入理解SystemUI
- 7.1 初識SystemUI
- 7.1.1 SystemUIService的啟動
- 7.1.2 狀態欄與導航欄的創建
- 7.1.3 理解IStatusBarService
- 7.1.4 SystemUI的體系結構
- 7.2 深入理解狀態欄
- 7.2.1 狀態欄窗口的創建與控件樹結構
- 7.2.2 通知信息的管理與顯示
- 7.2.3 系統狀態圖標區的管理與顯示
- 7.2.4 狀態欄總結
- 7.3 深入理解導航欄
- 7.3.1 導航欄的創建
- 7.3.2 虛擬按鍵的工作原理
- 7.3.3 SearchPanel
- 7.3.4 關于導航欄的其他話題
- 7.3.5 導航欄總結
- 7.4 禁用狀態欄與導航欄的功能
- 7.4.1 如何禁用狀態欄與導航欄的功能
- 7.4.2 StatusBarManagerService對禁用標記的維護
- 7.4.3 狀態欄與導航欄對禁用標記的響應
- 7.5 理解SystemUIVisibility
- 7.5.1 SystemUIVisibility在系統中的漫游過程
- 7.5.2 SystemUIVisibility發揮作用
- 7.5.3 SystemUIVisibility總結
- 7.6 本章小結
- 第8章 深入理解Android壁紙
- 8.1 初識Android壁紙
- 8.2深入理解動態壁紙
- 8.2.1啟動動態壁紙的方法
- 8.2.2壁紙服務的啟動原理
- 8.2.3 理解UpdateSurface()方法
- 8.2.4 壁紙的銷毀
- 8.2.5 理解Engine的回調
- 8.3 深入理解靜態壁紙-ImageWallpaper
- 8.3.1 獲取用作靜態壁紙的位圖
- 8.3.2 靜態壁紙位圖的設置
- 8.3.3 連接靜態壁紙的設置與獲取-WallpaperObserver
- 8.4 WMS對壁紙窗口的特殊處理
- 8.4.1 壁紙窗口Z序的確定
- 8.4.2 壁紙窗口的可見性
- 8.4.3 壁紙窗口的動畫
- 8.4.4 壁紙窗口總結
- 8.5 本章小結