原文地址:http://blog.csdn.net/huanxido/article/details/7879529
一、 概述
在Android系統中,從設計的角度來看,窗口管理系統是基於C/S模式的。整個窗口系統分爲服務端和客戶端兩大部分,客戶端負責請求創建窗口和使用窗口,服務端完成窗口的維護,窗口顯示等。
在Client端,並不是直接和WindowManagerService交互,而是直接和本地對象WindowManager交互,然後由WindowManager完成和WindowManagerService的交互。對於Android應用來說這個交互是透明的,應用不能感知到WindowManagerService的存在
二、 窗口的定義
在android的應用框架中,窗口主要分爲兩種:
第一種是應用窗口:一個activity有一個主窗口,彈出的對話框也有一個窗口,Menu菜單也是一個窗口。在同一個activity中,主窗口、對話框、Menu窗口之間通過該activity關聯起來。和應用相關的窗口表示類是PhoneWindow和Window,PhoneWindow繼承於Window,針對手機屏幕做了一些優化工作。PhoneWindow只是一個窗口封裝類,裏面核心的是mDecorView這個變量,mDecorView是一個頂層的View,窗口的添加就是通過調用getDecorView()獲取到mDecorView並且調用WindowManager.addView()把該View添加到WindowManager中。
第二種是公共界面的窗口:如最近運行對話框、關機對話框、狀態欄下拉欄、鎖屏界面等。這些窗口都是系統級別的窗口,不從屬於任何應用,和activity沒有任何關係。這種窗口沒有任何窗口類來封裝,直接調用WindowManager.addView()來把一個view添加到WindowManager中。
在應用初始化的時候,會首先生成一個Activity對象,此時該activity還沒有屬於他的一個窗口。緊接着通過調用attach()函數,在attach()函數裏面該activity會調用PolicyManager.makeNewWindow()創建一個新的PhoneWindow,然後在activity的onCreate()生命週期裏,一般應用都會調用setContentView()設置該activity的顯示界面。在setContentView()裏,框架會自動生成一個佈局,該佈局文件包含了如標題欄、ActionBar等元素,最重要的是包含了應用的contentView。這個佈局對應的就是PhoneWindow裏面的mDecorView。最後在activity將要顯示出來之前,通過getWindow().getDecorView()獲取到DecorView,並通過WindowManager.addView()把DecorView添加到WindowManager中。
Activity添加客戶端窗口時序圖
三、 窗口管理
Android的窗關管理是基於C/S模式的,並且使用獨立進程的方式實現。窗口管理的服務端WindowManagerService運行在獨立的進程system_server裏,當應用程序需要創建窗口時,通過進程通信的方式請求WindowManagerService創建窗口,由WindowManagerService嚮應用程序傳遞和窗口相關的交互消息。所有程序的窗口都在服務端管理,窗口的顯示和控制都在WindowManagerService裏處理。
WindowManagerService主要完成了以下幾部分功能:
1. 窗口的添加和刪除
2. 窗口的顯示和隱藏控制
3. Z-order順序管理
4. 焦點窗口和焦點應用的管理
5. 輸入法窗口管理和牆紙窗口管理
6. 轉場動畫
7. 系統消息收集和分發
服務端的實現代碼是在/framework/base/services/java/com/android/server/wm/裏,核心的幾個類是:
WindowManagerService.java
WindowState.java
WindowToken.java
AppWindowToken.java
Session.java
InputManager.java
InputMonitor.java
類解釋:
WindowManagerService負責完成窗口的管理工作;
WindowState和客戶端窗口一一對應,應用調用WindowManager.addView()時,最終會在WindowManagerService添加一個WindowState與之一一對應。
WindowToken是一個句柄,保存了所有具有同一個token的WindowState。應用請求WindowManagerService添加窗口的時候,提供了一個token,該token標識了被添加窗口的歸屬,WindowManagerService爲該token生成一個WindowToken對象,所有token相同的WindowState被關聯到同一個WindowToken。如輸入法添加窗口時,會傳遞一個mCurrToken,牆紙服務添加窗口時,會傳遞一個newConn.mToken。
AppWindowToken繼承於WindowToken,專門用於標識一個Activity。AppWindowToken裏的token實際上就是指向了一個Activity。ActivityManagerService通知應用啓動的時候,在服務端生成一個token用於標識該Activity,並且把該token傳遞到應用客戶端,客戶端的Activity在申請添加窗口時,以該token作爲標識傳遞到WindowManagerService。同一個Activity中的主窗口、對話框窗口、菜單窗口都關聯到同一個AppWindowToken。
Session表示一個客戶端和服務端的交互會話。一般來說不同的應用通過不同的會話來和WindowManagerService交互,但是處於同一個進程的不同應用通過同一個Session來交互。
InputManager和InputMonitor負責上層的消息分發功能。
WindowManagerService內部的幾個重要成員變量:
ArrayList<WindowState> mWindows
HashMap<IBinder, WindowState> mWindowMap
ArrayList<WindowToken> mTokenList
ArrayList<AppWindowToken> mAppTokens
mWindows保存了系統中所有的WindowState;
mWindowMap保存了每個WindowState和客戶端窗口的映射關係,客戶端應用請求窗口操作時,通過mWindowMap查詢到對應的WindowState;
mTokenList保存了所有的WindowToken
mAppTokens保存了所有的AppWindowToken
窗口管理服務端主要類圖
一個Activity從啓動到添加窗口的整個流程如下:
ActivityManagerService在接收到啓動Activity請求時,首先生成一個token作爲該Activity的唯一標識。然後調用WindowManagerService向其添加一個AppWindowToken,此AppWindowToken封裝了Activity的token。接着AMS啓動應用客戶端進程並把token傳遞到該進程,在客戶端進程裏完成Activity的初始化。在Activity的attach()函數中,Activity完成PhoneWindow的創建,並且把token傳遞給PhoneWindow。在Activity調用WindowManager.addView()時,在WindowManager內部會把token和該View關聯,真正向WindowManagerService申請創建窗口的時候,再把token傳遞給WindowManagerService。WindowManagerService接收到創建窗口的請求的時候,通過mTokenMap查詢對應該token的AppWindowToken,如果爲空則拋出異常,否則創建一個WindowState並完成初始化工作和其他數據結構的調整工作。在這個過程中,token貫穿了服務端的AMS、WMS和客戶端的Activity、Window。
Activity啓動過程中創建窗口的時序圖
四、 WMS中服務端和客戶端的交互接口和數據結構
應用請求創建窗口時,和應用直接交互的是WindowManager對象。WindowManager只是一個接口,調用addView()創建窗口時正真交互的是WindowManagerImpl對象。WindowManagerImpl管理單個應用的所有本地窗口。應用調用addView()創建窗口時,WindowManagerImpl會生成一個ViewRoot對象與之相對應,並且把相應的參數LayoutParams保存起來。
addView()的執行流程如下:
(1) 檢查所添加的窗口是否已經添加過,不允許重複添加;
(2) 如果所添加窗口爲子窗口類型,找到其父窗口,並保存在內部變量中;
(3) 創建一個新的ViewRoot,並保存對應的View(DecorView)和LayoutParams;
(4) 調用ViewRoot的setView()方法,完成真正意義上的添加工作。
ViewRoot本質上是一個Handler,並且實現了ViewParent接口。ViewRoot的主要功能是:
1. 負責分發消息事件,如Key、Motion事件等;
2. 負責和WMS的交互,分發WMS的交互命令;
3. 作爲DecorView的parent,對DecorView進行draw、measure、layout等操作;
在addView()的第3、4步完成之後,ViewRoot就全權接管了和WMS的交互工作,DecorView不需要做任何交互動作。ViewRoot和WMS之間的雙向對話,主要是通過以下兩個數據結構進行的:
IWindowSession
IWindow
這兩個數據結構都是標準的aidl接口,用於進程之間的同步通信。IWindowSession負責ViewRoot到WMS的單向請求,IWindow則用於WMS回調ViewRoot。在ViewRoot對象內部,存在着一個IWindowSession的靜態成員和一個IWindow的非靜態成員,所以一個進程裏只有一個IWindowSession對象,但是可以有多個IWindow對象。
Window、WindowManager、DecorView、ViewRoot、IWindowSession、IWindowSession、WindowState、WindowManagerService之間的關係可用下圖來表示:
在ViewRoot的構造函數中,調用getWindowSession()初始化靜態成員sWindowSession和非靜態成員mWindow。在第4步調用setView()方法時,ViewRoot會調用sWindowSession.add()方法,把IWindow添加到WMS中,WMS就會生成一個WindowState與之一一對應,並且把IWindow對象保存到WindowState內部作爲回調的接口。之後所有WMS的命令,都會通過直接訪問IWindow接口,以消息的形式分發到ViewRoot,ViewRoot來完成相應的處理,或對DecorView進行操作,或完成後通過sWindowSession報告給WMS。
一個窗口從添加到顯示可用以下時序圖表示:
窗口添加過程時序圖
到此爲止,整個窗口管理系統整體架構可表示如下:
窗口管理系統整體架構圖
五、 WindowState和Surface
從Client端調用WindowManager的addView()方法到WMS完成WindowState的初始化,在這整個過程中,只是完成了一個窗口數據結構的創建,也就是說,到現在爲止,Client端的窗口和Server端的窗口已經建立了一種相對固定的連接關係,並且Client端和Server端之間能夠正常通信,WMS能夠透明的對Client端的窗口進行操作,同時WMS也能夠接收Client端窗口的命令,對WindowState進行相應的調整。
一個WindowState想要顯示在屏幕上,必須申請一個顯示緩存,這個顯示緩存的管理和維護是在底層圖形模塊實現的,在java層有一個操作的封裝對象Surface。WindowState申請到Surface對象之後,會將此Surface對象的相關數據拷貝到Client端的ViewRoot中,ViewRoot中也維護了一個Surface對象,實際上這兩個對象是指向同一塊顯示緩存。ViewRoot有了這塊顯示緩存的引用之後,即可以通過lockCanvas來獲取繪畫畫布,繪製完畢之後通過unlockAndPostCanvas來將繪製內容刷新到顯示緩存中。也就是說,Client端窗口和Server端窗口共用一個Surface,Client負責繪製Surface的內容,Server負責控制Surface在屏幕上的大小位置等。
ViewRoot通過IWindowSession的relayout()接口來向WMS發送請求命令,包括窗口的顯示和隱藏,窗口的佈局信息如位置大小,同時還會接收WMS的處理結果。WMS會根據屏幕大小和Client請求的佈局參數來決定窗口最終的佈局信息,同時也會根據Client請求的顯示隱藏命令來返回一個有效的或者無效的Surface對象。通常一個窗口的顯示過程爲:
1. Client請求顯示窗口,並且傳遞佈局參數;
2. WMS根據佈局參數,申請一個Surface對象並返回給Client;
3. Client對Surface進行繪畫操作,完成後告訴WMS;
4. WMS將Surface顯示在屏幕上,並且進行層級等相應調整;
窗口顯示過程時序圖
一個橫跨Activity、View、ViewRoot、IWindowSession、IWindow、WindowState、WindowManagerService、Surface的整體概念如下如所示:
窗口管理系統完整架構圖