盡管SystemUI的表現形式與普通的Android應用程序大相徑庭,但它卻是以一個APK的形式存在于系統之中,即它與普通的Android應用程序并沒有本質上的區別。無非是通過Android四大組件中的Activity、Service、BroadcastReceiver接受外界的請求并執行相關的操作,只不過它們所接受到的請求主要來自各個系統服務而已。
SystemUI包羅萬象,并且大部分功能之間相互獨立,比如RecentPanel、TakeScreenshotService等均是按需啟動,并在完成其既定任務后退出,這與普通的Activity以及Service別無二致。比較特殊的是狀態欄、導航欄等組件的啟動方式。它們運行于一個稱之為SystemUIService的一個Service之中。因此討論狀態欄與導航欄的啟動過程其實就是SystemUIService的啟動過程。
#### 1.SystemUIService的啟動時機
那么SystemUIService在何時由誰啟動的呢?作為一個系統級別的UI組件,自然要在系統的啟動過程中來尋找答案了。
在負責啟動各種系統服務的ServerThread中,當核心系統服務啟動完成后ServerThread會通過調用ActivityManagerService.systemReady()方法通知AMS系統已經就緒。這個systemReady()擁有一個名為goingCallback的Runnable實例作為參數。顧名思義,當AMS完成對systemReady()的處理后將會回調這一Runnable的run()方法。而在這一run()方法中可以找到SystemUI的身影:
**SystemServer.java-->ServerThread**
```
ActivityManagerService.self().systemReady(newRunnable() {
publicvoid run() {
// 調用startSystemUi()
if(!headless) startSystemUi(contextF);
......
}
}
```
進一步地,在startSystemUI()方法中:
**SystemServer.java-->ServerThread.startSystemUi()**
```
static final void startSystemUi(Context context) {
Intentintent = new Intent();
// 設置SystemUIService作為啟動目標
intent.setComponent(new ComponentName("com.android.systemui",
"com.android.systemui.SystemUIService"));
// 啟動SystemUIService
context.startServiceAsUser(intent, UserHandle.OWNER);
}
```
可見,當核心的系統服務啟動完畢后,ServerThread通過Context.startServiceAsUser()方法完成了SystemUIService的啟動。
#### 2.SystemUIService的創建
參考SystemUIService的onCreate()的實現:
**SystemUIService.java-->SystemUIService.onCreate()**
```
/* **①SERVICES數組定義了運行于SystemUIService之中的子服務列表。**當SystemUIService服務啟動
時將會依次啟動列表中所存儲的子服務 */
final Object[] SERVICES = new Object[] {
0,// 0號元素存儲的其實是一個字符串資源號,這個字符串資源存儲了實現了狀態欄/導航欄的類名
com.android.systemui.power.PowerUI.class,
com.android.systemui.media.RingtonePlayer.class,
};
public void onCreate() {
......
IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
try {
/* **② 根據IWindowManager.hasSystemNavBar()的返回值選擇一個合適的**
** 狀態欄與導航欄的實現** */
SERVICES[0] = wm.hasSystemNavBar()
? R.string.config_systemBarComponent
: R.string.config_statusBarComponent;
} catch(RemoteException e) {......}
finalint N = SERVICES.length;
//mServices數組中存儲了子服務的實例
mServices = new SystemUI[N];
for (inti=0; i<N; i++) {
Class cl = chooseClass(SERVICES[i]);
try{
// **③ 實例化子服務并將其存儲在mServices數組中**
mServices[i] = (SystemUI)cl.newInstance();
}catch (IllegalAccessException ex) {......}
// **④ 設置Context,并通過調用其start()方法運行它**
mServices[i].mContext = this;
mServices[i].start();
}
}
```
除了onCreate()方法之外,SystemUIService沒有其他有意義的代碼了。顯而易見,SystemUIService是一個容器。在其啟動時,將會逐個實例化定義在SERVICIES列表中的繼承自SystemUI抽象類的子服務。在調用了子服務的start()方法之后,SystemUIService便不再做任何其他的事情,任由各個子服務自行運行。而狀態欄導航欄則是這些子服務中的一個。
值得注意的是,onCreate()方法根據IWindowManager.hasSystemNavBar()方法的返回值為狀態欄/導航欄選擇了不同的實現。進行這一選擇的原因為了能夠在大尺寸的設備中更有效地利用屏幕空間。在小屏幕設備如手機中,由于屏幕寬度有限,Android采取了狀態欄與導航欄分離的布局方案,也就是說導航欄與狀態欄占用了更多的垂直空間,使得導航欄的虛擬按鍵尺寸足夠大以及狀態欄的信息量足夠多。而在大屏幕設備如平板電腦中,由于屏幕寬度比較大,足以在一個屏幕寬度中同時顯示足夠大的虛擬按鍵以及足夠多的狀態欄信息量,此時可以選擇將狀態欄與導航欄功能集成在一起成為系統欄作為大屏幕下的布局方案,以節省對垂直空間的占用。
hasSystemNavBar()的返回值取決于PhoneWindowManager.mHasSystemNavBar成員的取值。因此在PhoneWindowManager.setInitialDisplaySize()方法中可以得知Android在兩種布局方案中進行選擇的策略。
**PhoneWindowManager.java-->PhoneWindowManager.setInitialDisplaySize()**
```
public void setInitialDisplaySize(Display display,int width
, intheight, int density) {
......
// **① 計算屏幕短邊的DP寬度**
intshortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / density;
// **② 屏幕寬度在720dp以內時,使用分離的布局方案**
if(shortSizeDp < 600) {
mHasSystemNavBar= false;
mNavigationBarCanMove = true;
} elseif (shortSizeDp < 720) {
mHasSystemNavBar = false;
mNavigationBarCanMove = false;
}
......
}
```
在SystemUI中,分離布局方案的實現者是PhoneStatusBar,而集成布局方案的實現者則是TabletStatusBar。二者的本質功能是一致的,即提供虛擬按鍵、顯示通知信息等,區別僅在于布局的不同、以及由此所衍生出的定制行為而已。因此不難想到,它們是從同一個父類中繼承出來的。這一父類的名字是BaseStatusBar。本章將主要介紹PhoneStatusBar的實現,讀者可以類比地對TabletStatusBar進行研究。
- 前言
- 推薦序
- 第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 本章小結