標籤:
http://www.cnblogs.com/samchen2009/p/3364327.html
Android的GUI系統是Android最重要也最複雜的系統之一。它包括以下部分:
- 視窗和圖形系統 - Window and View Manager System.
- 顯示合成系統 - Surface Flinger
- 使用者輸入系統 - InputManager System
- 應用程式框架系統 - Activity Manager System.
它們之間的關係如所示
只有對這些系統的功能和工作原理有基本的瞭解,我們才能夠解答一些經常縈繞在腦海裡的問題,比如說:
- Activity啟動過程是怎樣的?Activity的onXXX()在後台都做了什麼工作?Activity的show()和hide()是如何控制的?
- Surface是什麼時候被誰建立的?
- Android是如何把一個個的控制項畫到Surface上的?然後顯示到手機螢幕上的?
- 應用程式視窗是如何擷取焦點的?使用者的按鍵(觸控螢幕或鍵盤)是怎樣傳遞到當前的視窗?
- Android是一個多視窗的系統嗎?
- Android是怎麼支援多屏互動的?(Wifi Display)
- IME視窗到底屬於哪個進程?為什麼不管什麼應用,只要有輸入框的地方都能彈出它?
本文將從架構和流程角度出發,試圖對Android的GUI系統做一個簡要但全面的介紹,希望藉此能夠協助大家找到上述問題的答案。
所有的內容可以濃縮在下面這張圖裡,裡面有很多的名稱和概念我們需要事先解釋一下:
1. Window, PhoneWindow 和 Activity
- Activity 是 Android 應用的四大組件之一 (Activity, Service, Content Provider, Broadcast Receiver), 也是唯一一個與使用者直接互動的組件。
- Window 在 不同的地方有著不同的含義。在Activity裡,Window 是一個抽象類別,代表了一個矩形的不可見的容器,裡面布局著若干個可視的地區(View). 每個Activity都會有一個Window類成員變數,mWindow. 而在WindowManagerService裡,Window指的是WindowState對象,可以看出,WindowState與一個 ViewRootImpl裡的mWindow對象相對應。所以說,WindowManagerService裡管理的Window其實是 Acitivity的ViewRoot。我們下面提到的Window,如果沒有做特殊說明,均指的是WindowManagerService裡的 ‘Window‘ 概念,即一個特定的顯示地區。從使用者角度來看,Android是個多視窗的作業系統,不同尺寸的視窗地區根據尺寸,位置,z-order及是否透明等參數 疊加起來一起並最終呈現給使用者。這些視窗既可以是來自一個應用,也可以來自與多個應用,這些視窗既可以顯示在一個平面,也可以是不同的平面。總而言之,窗 口是有層次的顯示地區,每個視窗在底層最終體現為一個個的矩形Buffer, 這些Buffer經過計算合成為一個新的Buffer,最終交付Display系統進行顯示。為了輔助最後的視窗管理,Android定義了一些不同的窗 口類型:
- 應用程式視窗 (Application Window): 包括所有應用程式自己建立的視窗,以及在應用起來之前系統負責顯示的視窗。
- 子視窗(Sub Window):比如應用自訂的對話方塊,或者IME視窗,子視窗必須依附於某個應用視窗(設定相同的token)。
- 系 統視窗(System Window): 系統設計的,不依附於任何應用的視窗,比如說,狀態列(Status Bar), 導覽列(Navigation Bar), 壁紙(Wallpaper), 來電顯示視窗(Phone),鎖屏視窗(KeyGuard), 資訊提示視窗(Toast), 音量調整視窗,滑鼠游標等等。
- PhoneWindow 是Activity Window的擴充,是為手機或平板裝置專門設計的一個視窗布局方案,就像大家在手機上看到的,一個PhoneWindow的布局大致如下:
2. View, DecorView, ViewGroup, ViewRoot
View 是一個矩形的可見地區。
ViewGroup 是一種特殊的View, 它可以包含其他View並以一定的方式進行布局。Android支援的布局有FrameLayout, LinearLayout, RelativeLayout 等。
DecorView 是FrameLayout的子類,FrameLayout 也叫單幀布局,是最簡單的一種布局,所有的子View在垂直方向上按照先後順序依次疊加,如果有重疊部分,後面的View將會把前面的View擋住。我們 經常看到的彈出框,把後面的視窗擋住一部分,就是用的FrameLayout布局。Android的視窗基本上用的都是FrameLayout布局, 所以DecorView也就是一個Activity Window的頂級View, 所有在視窗裡顯示的View都是它的子View.
ViewRoot . 我們可以定義所有被addView()調用的View是ViewRoot, 因為介面將會產生一個ViewRootImpl 對象,並儲存在WindowManagerGlobal的mRoots[] 數組裡。一個程式可能有很多了ViewRoot(只要多次調用addView()), 在WindowManagerService端看來,就是多個Window。但在Activity的預設實現裡,只有mDecorView 通過addView 添加到WindowManagerService裡( 見如下代碼)
//frameworks/base/core/java/android/app/Activity.java void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); }
因此一般情況下,我們可以說,一個應用可以有多個Activity,每個 Activity 一個Window(PhoneWindow), 每個Window 有一個DecorView, 一個ViewRootImpl, 對應在WindowManagerService 裡有一個Window(WindowState).
3. ViewRootImple, WindowManagerImpl, WindowManagerGlobals
WindowManagerImpl: 實現了WindowManager 和 ViewManager的介面,但大部分是調用WindowManagerGlobals的介面實現的。
WindowManagerGlobals: 一個SingleTon對象,對象裡維護了三個數組:
- mRoots[ ]: 存放所有的ViewRootImpl
- mViews[ ]: 存放所有的ViewRoot
- mParams[ ]: 存放所有的LayoutParams.
同時,它還維護了兩個全域IBinder對象,用於訪問WindowManagerService 提供的兩套介面:
- IWindowManager: 主要介面是OpenSession(), 用於在WindowManagerService 內部建立和初始化Session, 並返回IBinder對象。
- ISession: 是Activity Window與WindowManagerService 進行對話的主要介面.
ViewRootImpl:
ViewRootImpl 在整個Android的GUI系統中佔據非常重要的位置,如果把Activity和View 看作 ‘MVC‘ 中的V, 把各種後台服務看作Modal,ViewRootImpl 則是‘MVC‘ 中的‘C‘ - Controller. Controller在MVC架構裡承擔著承上啟下的作用,一般來說它的邏輯最為複雜。從可以看到,ViewRootImpl 與 使用者輸入系統(接收使用者按鍵,觸控螢幕輸入), 視窗系統(複雜視窗的布局,重新整理,動畫),顯示合成系統(包括定時器Choreograph, SurfaceFlinger), 乃至Audio系統(音效輸出)等均有密切的關聯。研究ViewRootImpl 是研究Android整個視窗系統的核心和切入點,我們將在後面詳細討論ViewRootImpl的實現和作用。
三 者( ViewRootImpl, WindowManagerImpl, WindowManagerGlobal) 都存在於應用(有Activity)的進程空間裡,一個Activity對應一個WindowManagerImpl, 一個DecorView(ViewRoot),以及一個ViewRootImpl (上面說過,實際一個Activity只有一個DecorView),而WindowManagerGlobals是一個全域對象,一個應用永遠只有一 個。
注意的是,在某些情況下,一個應用可能會有幾個ViewRootImpl對象,比如說ANR是彈出的對話方塊,或是網頁裡面一個視頻視窗 (SurfaceView), 在WindowManagerService看來,它們也是一個視窗。同時,SystemServer的進程空間也有自己的 WindowManagerGlobals 和若干個ViewRoot, 因為WindowManagerService 內部也會管理某些系統視窗,如手機頂部的StatusBar, 手機底部的NavigationBar, 以及 鎖屏(KeyGuard)視窗,這些視窗不屬於某個特定的Activity。
4. WindowManager, WindowManagerService 和 WindowManagerPolicyService
WindowManager: 是一個介面類,定義了一些介面來管理Acitivity裡的視窗。WindowManager 是Android應用進程空間裡的一個對象,不提供IPC服務。
WindowManagerService: 是SystemServer進程裡的一個Service,它的主要功能有
- 窗 口的顯示重新整理。這裡的‘Window‘ 其實是ViewRoot, 和上面WindowManager管理的‘Window‘ 是不一樣的,前者是實實在在要進行顯示的‘視窗’, 而後者只是一個View的容器,並不會顯示出來。大部分情況下,Android同時只有一個Activity工作,但這並不意思著只有一個Window被 顯示,Android可能會同時顯示來自相同或不同應用的多個Window,比如說,螢幕的上方有一個狀態列,最下方有一個導覽列,有時會彈出一些對話 框,背景可能會顯示牆紙,在應用啟動過程中,會有動畫效果,這個時候兩個Activity的視窗會有所變形且同時顯示出來,這一切都需要 WindowManager來控制何時,何地,以何種方式將所有的視窗整合在一起顯示。
- 預先處理使用者輸入時間(GlobalKey? SystemKey), 並分發給合適的視窗進行處理。
- 輸出顯示(Display)管理。包括WifiDisplay.
WindowManagerService 是Android Framework裡最為龐大複雜的模組之一,我們後面會從各個方面對它進行儘可能詳細的分析。
5. Token, WindowToken, AppWindowToken, ApplicationToken, appToken
Token在英語中表示標記,信物的意思,在代碼中,有點類似Handle,Cookie, ID, 用來標識某個特定的對象。在Android的視窗系統中,有很多的’Token‘, 它們代表著不同的含義。
WindowToken: 是在WindowManagerService 中定義的一個基類,顧名思義,它是用來標識某一個視窗。和下面的appWindowToken相比, 它不屬於某個特定的Activity, 比如說IME視窗,狀態列視窗等等。
appWindowToken: 顧名思義,它是用來標識app, 跟準確的說法,是用來標識某個具體的Activity.
ApplicationToken: 指的是ActivityRecord 類裡的Token子類。appWindowToken裡的appToken也就是它。
appToken: 和applicationToken是一個意思。
下 圖描繪了各個Token之間的關係。一個Token下面帶一個WindowList隊列,裡面存放著隸屬與這個Token的所有視窗。當一個Window 加入WindowManagerService 管理時,必須指定他的Token值,WindowManagerService維護著一個Token與WindowState的索引值Hash表。
通過 ‘dumpsys window tokens‘ 我們可以列出WindowManagerService當前所有的Token 和 視窗。比如,
WINDOW MANAGER TOKENS (dumpsys window tokens) All tokens: WindowToken{4ea639c4 null}: //token = NULL windows=[Window{4ea7670c u0 Application Not Responding: jackpal.androidterm}, Window{4ea63a08 u0 Keyguard}] windowType=-1 hidden=false hasVisible=true AppWindowToken{4eb29760 token=Token{4eb289d4 ActivityRecord{4ea87a20 u0 com.android.launcher/com.android.launcher2.Launcher}}}: //Launcher2 windows=[Window{4ea837c8 u0 com.android.launcher/com.android.launcher2.Launcher}] windowType=2 hidden=true hasVisible=true ... WindowToken{4eb1fd48 [email protected]}: windows=[Window{4ea92b78 u0 PopupWindow:4ea0240c}] //對話方塊 windowType=-1 hidden=false hasVisible=false AppWindowToken{4eb5d6c0 token=Token{4ea35074 ActivityRecord{4ea68590 u0 jackpal.androidterm/.Term}}}: windows=[Window{4eb314e4 u0 jackpal.androidterm/jackpal.androidterm.Term}] windowType=2 hidden=false hasVisible=true app=true
6. Surface, Layer 和 Canvas, SurfaceFlinger, Region, LayerStack
在Android中,Window與Surface一一對應。 如果說Window關心的是層次和布局,是從設計者角度定義的類,Surface則從實現角度出發,是工程師關係和考慮的類。Window的內容是變化 的,Surface需要有空間來記錄每個時刻Window的內容。在Android的SurfaceFlinger實現裡,通常一個Surface有兩塊 Buffer, 一塊用於繪畫,一塊用於顯示,兩個Buffer按照固定的頻率進行交換,從而實現Window的動態重新整理。
Layer是SurfaceFlinger 進行合成的基本操作單元。Layer在應用請求建立Surface的時候在SurfaceFlinger內部建立,因此一個Surface對應一個 Layer, 但注意,Surface不一定對應於Window,Android中有些Surface並不跟某個Window相關,而是有程式直接建立,比如說 StrictMode, 一塊紅色的背景,用於提示示Java代碼中的一些異常, 還有SurfaceView, 用於顯示有硬體輸出的視頻內容等。
當多個Layer進行合成的時候,並不是整個Layer的空間都會被完全顯示,根據這個Layer最終的顯示效果,一個Layer可以被劃分成很多的Region, Android SurfaceFlinger 定義了以下一些Region類型:
- TransparantRegion: 完全透明的地區,在它之下的地區將被顯示出來。
- OpaqueRegion: 完全不透明的地區,是否顯示取決於它上面是否有遮擋或是否透明。
- VisibleRegion: 可見地區,包括完全不透明無遮擋地區或半透明地區。 visibleRegion = Region - above OpaqueRegion.
- CoveredRegion: 被遮擋地區,在它之上,有不透明或半透明地區。
- DirtyRegion: 可見部分改變地區,包括新的被遮擋地區,和新的露出地區。
Android 系統支援多種顯示裝置,比如說,輸出到手機螢幕,或者通過WiFi 投射到電視螢幕。Android用Display類來表示這樣的裝置。不是所有的Layer都會輸出到所有的Display, 比如說,我們可以只將Video Layer投射到電視, 而非整個螢幕。LayerStack 就是為此設 計,LayerStack 是一個Display 對象的一個數值, 而類Layer裡也有成員變數mLayerStack, 只有兩者的mLayerStack 值相同,Layer才會被輸出到給該Display裝置。所以LayerStack 決定了每個Display裝置上可以顯示的Layer數目。
SurfaceFlinger的工作內容,就是定期檢查所有Layer的參數更新(LayerStack等),計算新的DirtyRegion, 然後將結果推送給底層顯示驅動進行顯示。這裡面有很多的細節,我們將在另外的章節專門研究。
上面描述的幾個概念,均是針對於顯示這個層面,更多是涉及到中下層模組,應用程式層並不參與也無需關心。對於應用而言,它關心的是如何將內容畫出來。Canvas 是Java層定義的一個類,它對應與Surface上的某個地區並提供了很多的2D繪製函數(藉助於底層的Skia或OpenGL)。應用只需通過 LockCanvas() 來擷取一個Canvas對象,並調用它的繪畫方法,然後 unLockCanvasAndPost()來通知底層將更新內容進行顯示。當然,並不是所有應用程式都需要直接操作Canva, 事實上只有少量應用需要直接操作Canvas, Android提供了很多封裝好的控制項 Widget,應用只需提供素材,如文字,圖片,屬性等等,這些控制項會調用Canvas提供的介面幫使用者完成繪製工作。
7. SurfaceFlinger, HWComposer, OpenGL 和 Display
SurfaceFlinger 是一個獨立的Service, 它接收所有Window的Surface作為輸入,根據ZOrder, 透明度,大小,位置等參數,計算出每個Surface在最終合成映像中的位置,然後交由HWComposer或OpenGL產生最終的顯示Buffer, 然後顯示到特定的顯示裝置上。
HWComposer 是 Andrid 4.0後推出的新特性,它定義一套HAL層介面,然後各個晶片廠商根據各種硬體特點來實現。它的主要工作是將SurfaceFlinger計算好的Layer的顯示參數最終合成到一個顯示Buffer上。注意的是,Surface Flinger 並非是HWComposer的唯一輸入,有的Surface 不由Android的WindowManager 管理,比如說網路攝影機的預覽輸入Buffer, 可以有硬體直接寫入,然後作為HWComposer的輸入之一與SurfaceFlinger的輸出做最後的合成。
OpenGL 是一個2D/3D圖形庫,需要底層硬體(GPU)和驅動的支援。在Android 4.0後,它取代Skia成為Android 的2D 繪圖圖形庫,大部分的控制項均改用它來實現,應用程式也可以直接調用OpenGl函數來實現複雜的圖形介面。
Display 是Android 對輸出顯示裝置的一個抽象,傳統的Display 裝置是手機上的LCD屏,在Andrid 4.1 後,Android 對SurfaceFlinger 進行了大量的改動,從而支援其他外部輸入裝置,比如HDMI, Wifi Display 等等。Display的輸入是根據上面的LayerStack值進行過濾的所有Window的Surface, 輸出是和顯示裝置尺寸相同的Buffer, 這個Buffer 最終送到了硬體的FB裝置,或者HDMI裝置,或者遠處的Wifi Display Sink裝置進行顯示。輸入到輸出這條路徑上有SurfaceFlinger, OpenGL 和 HWComposer。
有了上述概念的解析,對Android的GUI 系統應該有了一些模糊的認識,接下來我們將按下面的順序將一步步深入其中的細節。
圖解Android - Android GUI 系統 (1) - 概論