經過上一節的介紹,讀者應該對WMS的窗口管理有了一個感性的認識。從這一節開將深入WMS的內部去剖析其工作流程。
根據前述內容可知,SampleWindow添加窗口的函數是IWindowSession.add()。IWindowSession是WMS與客戶端交互的一個代理,add則直接調用到了WMS的addWindow()函數。我們將從這個函數開始WMS之旅。本小節只討論它的前半部分。
>[info] **注意**: 由于篇幅所限,本章不準備討論removeWindow的實現。
**WindowManagerService.java::WindowManagerService.addWindow()Part1**
```
public int addWindow(Session session, IWindowclient, int seq,
WindowManager.LayoutParams attrs, int viewVisibility,int displayId
Rect outContentInsets, InputChannel outInputChannel) {
// 首先檢查權限,沒有權限的客戶端不能添加窗口
intres = mPolicy.checkAddPermission(attrs);
......
// 當為某個窗口添加子窗口時,attachedWindow將用來保存父窗口的實例
WindowState attachedWindow = null;
//win就是即將被添加的窗口了
WindowState win = null;
......
finalint type = attrs.type;
synchronized(mWindowMap){
......
//①獲取窗口要添加到的DisplayContent
/* 在添加窗口時,必須通過displayId參數指定添加到哪一個DisplayContent。
SampleWindow例子沒有指定displayId參數,Session會替SampleWindow選擇
DEFAULT_DISPLAY,也就是手機屏幕 */
finalDisplayContent displayContent = getDisplayContentLocked(displayId);
if(displayContent == null) {
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
......
// 如果要添加的窗口是另一個的子窗口,就要求父窗口必須已經存在
// 注意, attrs.type表示了窗口的類型,attrs.token則表示了窗口所隸屬的對象
// 對于子窗口來說,attrs.token表示了父窗口
if(type >= FIRST_SUB_WINDOW &&.type <= LAST_SUB_WINDOW) {
attachedWindow = windowForClientLocked(null, attrs.token, false);
if (attachedWindow == null) {
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
//在這里還可以看出WMS要求窗口的層級關系最多為兩層
if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW
&&attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}
booleanaddToken = false;
// **②WindowToken出場!**根據客戶端的attrs.token取出已注冊的WindowToken
WindowToken token = mTokenMap.get(attrs.token);
// 下面的if語句塊初步揭示了WindowToken和窗口之間的關系
if(token == null) {
// 對于以下幾種類型的窗口,必須通過LayoutParams.token成員為其指定一個已經
// 添加至WMS的WindowToken
if (type >= FIRST_APPLICATION_WINDOW
&& type<= LAST_APPLICATION_WINDOW) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (type == TYPE_INPUT_METHOD) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (type == TYPE_WALLPAPER) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (type == TYPE_DREAM) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
// 其他類型的窗口則不需要事先向WMS添加WindowToken因為WMS會在這里隱式地創
// 建一個。注意最后一個參數false,這表示此WindowToken由WMS隱式創建。
token = new WindowToken(this, attrs.token, -1, false);
addToken = true;
} else if (type >= FIRST_APPLICATION_WINDOW
&&type <= LAST_APPLICATION_WINDOW) {
// 對于APPLICATION類型的窗口,要求對應的WindowToken的類型也為APPLICATION
// 并且是WindowToken的子類:AppWindowToken
AppWindowToken atoken = token.appWindowToken;
if (atoken == null) {
return WindowManagerImpl.ADD_NOT_APP_TOKEN;
} else if (atoken.removed) {
returnWindowManagerImpl.ADD_APP_EXITING;
}
if (type==TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn){
return WindowManagerImpl.ADD_STARTING_NOT_NEEDED;
}
} else if (type == TYPE_INPUT_METHOD) {
// 對于其他幾種類型的窗口也有類似的要求:窗口類型必須與WindowToken的類型一致
if (token.windowType != TYPE_INPUT_METHOD) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (type == TYPE_WALLPAPER) {
if (token.windowType != TYPE_WALLPAPER) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (type == TYPE_DREAM) {
if (token.windowType != TYPE_DREAM) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
}
// ③WMS為要添加的窗口創建了一個WindowState對象
// 這個對象維護了一個窗口的所有狀態信息
win= new WindowState(this, session, client, token,
attachedWindow,seq, attrs, viewVisibility, displayContent);
......
// WindowManagerPolicy出場了。這個函數的調用會調整LayoutParams的一些成員的取值
mPolicy.adjustWindowParamsLw(win.mAttrs);
res= mPolicy.prepareAddWindowLw(win, attrs);
if(res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
// 接下來將剛剛隱式創建的WindowToken添加到mTokenMap中去。通過這行代碼應該
//讀者應該能想到,所有的WindowToken都被放入這個HashTable中
......
if(addToken) {
mTokenMap.put(attrs.token, token);
}
win.attach();
// 然后將WindowState對象加入到mWindowMap中
mWindowMap.put(client.asBinder(),win);
// 剩下的代碼稍后再做分析
......
}
}
```
addWindow()函數的前段代碼展示了三個重要的概念,分別是WindowToken、WindowState以及DisplayContent。并且在函數開始處對窗口類型的檢查判斷也初步揭示了它們之間的關系:除子窗口外,添加任何一個窗口都必須指明其所屬的WindowToken;窗口在WMS中通過一個WindowState實例進行管理和保管。同時必須在窗口中指明其所屬的DisplayContent,以便確定窗口將被顯示到哪一個屏幕上。
- 前言
- 推薦序
- 第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 本章小結