ViewRootImpl創建于WindowManagerGlobal的addView()方法中,而調用addView()方法的線程即是此ViewRootImpl所掌控的控件樹的UI線程。ViewRootImpl的構造主要是初始化了一些重要的成員,事先對這些重要的成員有個初步的認識對隨后探討ViewRootImpl的工作原理有很大的幫助。其構造函數代碼如下:
**ViewRootImpl.java-->ViewRootImpl.ViewRootImpl()**
```
public ViewRootImpl(Context context, Displaydisplay) {
/* ① 從WindowManagerGlobal中獲取一個IWindowSession的實例。它是ViewRootImpl和
WMS進行通信的代理 */
mWindowSession= WindowManagerGlobal.getWindowSession(context.getMainLooper());
// **②保存參數display**,在后面setView()調用中將會把窗口添加到這個Display上
mDisplay= display;
CompatibilityInfoHolder cih = display.getCompatibilityInfo();
mCompatibilityInfo = cih != null ? cih : new CompatibilityInfoHolder();
/* **③ 保存當前線程到mThread。**這個賦值操作體現了創建ViewRootImpl的線程如何成為UI主線程。
在ViewRootImpl處理來自控件樹的請求時(如請求重新布局,請求重繪,改變焦點等),會檢
查發起請求的thread與這個mThread是否相同。倘若不同則會拒絕這個請求并拋出一個異常*/
mThread= Thread.currentThread();
......
/* **④ mDirty用于收集窗口中的無效區域。**所謂無效區域是指由于數據或狀態發生改變時而需要進行重繪
的區域。舉例說明,當應用程序修改了一個TextView的文字時,TextView會將自己的區域標記為無效
區域,并通過invalidate()方法將這塊區域收集到這里的mDirty中。當下次繪制時,TextView便
可以將新的文字繪制在這塊區域上 */
mDirty =new Rect();
mTempRect = new Rect();
mVisRect= new Rect();
/* **⑤ mWinFrame,描述了當前窗口的位置和尺寸。**與WMS中WindowState.mFrame保持著一致 */
mWinFrame = new Rect();
/* ⑥ 創建一個W類型的實例,W是IWindow.Stub的子類。即它將在WMS中作為新窗口的ID,以及接
收來自WMS的回調*/
mWindow= new W(this);
......
/* **⑦ 創建mAttachInfo。**mAttachInfo是控件系統中很重要的對象。它存儲了此當前控件樹所以貼附
的窗口的各種有用的信息,并且會派發給控件樹中的每一個控件。這些控件會將這個對象保存在自己的
mAttachInfo變量中。mAttachInfo中所保存的信息有WindowSession,窗口的實例(即mWindow),
ViewRootImpl實例,窗口所屬的Display,窗口的Surface以及窗口在屏幕上的位置等等。所以,當
要需在一個View中查詢與當前窗口相關的信息時,非常值得在mAttachInfo中搜索一下 */
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display,this, mHandler, this);
/* **⑧ 創建FallbackEventHandler。**這個類如同PhoneWindowManger一樣定義在android.policy
包中,其實現為PhoneFallbackEventHandler。FallbackEventHandler是一個處理未經任何人
消費的輸入事件的場所。在6.5.4節中將會介紹它 */
mFallbackEventHandler =PolicyManager.makeNewFallbackEventHandler(context);
......
/* ⑨ 創建一個依附于當前線程,即主線程的Choreographer,用于通過VSYNC特性安排重繪行為 */
mChoreographer= Choreographer.getInstance();
......
}
```
在構造函數之外,還有另外兩個重要的成員被直接初始化:
- mHandler,類型為ViewRootHandler,一個依附于創建ViewRootImpl的線程,即主線程上的,用于將某些必須主線程進行的操作安排在主線程中執行。mHandler與mChoreographer的同時存在看似有些重復,其實它們擁有明確不同的分工與意義。由于mChoreographer處理消息時具有VSYNC特性,因此它主要用于處理與重繪相關的操作。但是由于mChoreographer需要等待VSYNC的垂直同步事件來觸發對下一條消息的處理,因此它處理消息的及時性稍遜于mHandler。而mHandler的作用,則是為了將發生在其他線程中的事件安排在主線程上執行。所謂發生在其他線程中的事件是指來自于WMS,由繼承自IWindow.Stub的mWindow引發的回調。由于mWindow是一個Binder對象的Bn端,因此這些回調發生在Binder的線程池中。而這些回調會影響到控件系統的重新測量、布局與繪制,因此需要此Handler將回調安排到主線程中。
說明 mHandler與mThread兩個成員都是為了單線程模型而存在的。Android的UI操作不是線程安全的,而且很多操作也是建立在單線程的假設之上(如scheduleTraversals())。采用單線程模型的目的是降低系統的復雜度,并且降低鎖的開銷。
- mSurface,類型為Surface。采用無參構造函數創建的一個Surface實例。mSurface此時是一個沒有任何內容的空殼子,在 WMS通過relayoutWindow()為其分配一塊Surface之前尚不能實用。
- mWinFrame、mPendingContentInset、mPendingVisibleInset以及mWidth,mHeight。這幾個成員存儲了窗口布局相關的信息。其中mWinFrame、mPendingConentInsets、mPendingVisibleInsets與窗口在WMS中的Frame、ContentInsets、VisibleInsets是保持同步的。這是因為這3個成員不僅會作為 relayoutWindow()的傳出參數,而且ViewRootImpl在收到來自WMS的回調IWindow.Stub.resize()時,立即更新這3個成員的取值。因此這3個成員體現了窗口在WMS中的最新狀態。與mWinFrame中的記錄窗口在WMS中的尺寸不同的是,mWidth/mHeight記錄了窗口在ViewRootImpl中的尺寸,二者在絕大多數情況下是相同的。當窗口在WMS中被重新布局而導致尺寸發生變化時,mWinFrame會首先被IWindow.Stub.resize()回調更新,此時mWinFrame便會與mWidth/mHeight產生差異。此時ViewRootImpl即可得知需要對控件樹進行重新布局以適應新的窗口變化。在布局完成后,mWidth/mHeight會被賦值為mWinFrame中所保存的寬和高,二者重新統一。在隨后分析performTraversals()方法時,讀者將會看到這一處理。另外,與mWidth/mHeight類似,ViewRootImpl也保存了窗口的位置信息Left/Top以及ContentInsets/VisibleInsets供控件樹查詢,不過這四項信息被保存在了mAttachInfo中。
ViewRootImpl的在其構造函數中初始化了一系列的成員變量,然而其創建過程仍未完成。僅在為其指定了一個控件樹進行管理,并向WMS添加了一個新的窗口之后,ViewRootImpl承上啟下的角色才算完全確立下來。因此需要進一步分析ViewRootImpl.setView()方法。
**ViewRootImp.java-->ViewRootImpl.setView()**
```
public void setView(View view,WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
// **① mView保存了控件樹的根**
mView = view;
......
// ②mWindowAttributes保存了窗口所對應的LayoutParams
mWindowAttributes.copyFrom(attrs);
......
/* 在添加窗口之前,先通過requestLayout()方法在主線程上安排一次“遍歷”。所謂
“遍歷”是指ViewRootImpl中的核心方法performTraversals()。這個方法實現了對
控件樹進行測量、布局、向WMS申請修改窗口屬性以及重繪的所有工作。由于此“遍歷”
操作對于初次遍歷做了一些特殊處理,而來自WMS通過mWindow發生的回調會導致一些屬性
發生變化,如窗口的尺寸、Insets以及窗口焦點等,從而有可能使得初次“遍歷”的現場遭
到破壞。因此,需要在添加窗口之前,先發送一個“遍歷”消息到主線程。
在主線程中向主線程的Handler發送消息如果使用得當,可以產生很精妙的效果。例如本例
中可以實現如下的執行順序:添加窗口->初次遍歷->處理來自WMS的回調 */
requestLayout();
/***③ 初始化mInputChannel。**參考第五章,InputChannel是窗口接受來自InputDispatcher
的輸入事件的管道。 注意,僅當窗口的屬性inputFeatures不含有
INPUT_FEATURE_NO_INPUT_CHANNEL時才會創建InputChannel,否則mInputChannel
為空,從而導致此窗口無法接受任何輸入事件 */
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
try {
......
/* 將窗口添加到WMS中。完成這個操作之后,mWindow已經被添加到指定的Display中去
而且mInputChannel(如果不為空)已經準備好接受事件了。只是由于這個窗口沒有進行
過relayout(),因此它還沒有有效的Surface可以進行繪制 */
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mInputChannel);
} catch (RemoteException e) {......} finally { ...... }
......
if (res < WindowManagerGlobal.ADD_OKAY) {
// 錯誤處理。窗口添加失敗的原因通常是權限問題,重復添加,或者tokeen無效
}
......
/*④ 如果mInputChannel不為空,則創建mInputEventReceiver,用于接受輸入事件。
注意第二個參數傳遞的是Looper.myLooper(),即mInputEventReceiver將在主線程上
觸發輸入事件的讀取與onInputEvent()。這是應用程序可以在onTouch()等事件響應中
直接進行UI操作等根本原因。
*/
if (mInputChannel != null) {
......
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
/* ViewRootImpl將作為參數view的parent。所以,ViewRootImpl可以從控件樹中任何一個
控件開始,通過回溯getParent()的方法得到 */
view.assignParent(this);
......
}
}
}
```
至此,ViewRootImpl所有重要的成員都已經初始化完畢,新的窗口也已經添加到WMS中。ViewRootImpl的創建過程是由構造函數和setView()方法兩個環節構成的。其中構造函數主要進行成員的初始化,setView()則是創建窗口、建立輸入事件接收機制的場所。同時,觸發第一次“遍歷”操作的消息已經發送給主線程,在隨后的第一次“遍歷”完成后,ViewRootImpl將會完成對控件樹的第一次測量、布局,并從WMS獲取窗口的Surface以進行控件樹的初次繪制工作。
在本節的最后,通過圖 6 – 4對ViewRootImpl中的重要成員進行了分類整理。
:-: 
圖 6 - 4 ViewRootImpl中的主要成員
- 前言
- 推薦序
- 第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 本章小結