在前兩文中,咱們分析了Activity組件的窗口對象和視圖對象的建立過程。Activity組件在其窗口對象和視圖對象建立完成以後,就會請求與WindowManagerService創建一個鏈接,即請求WindowManagerService爲其增長一個WindowState對象,用來描述它的窗口狀態。在本文中,咱們就詳細分析Activity組件與WindowManagerService的鏈接過程。java
咱們從兩方面來看Activity組件與WindowManagerService服務之間的鏈接。一方面是從Activity組件到WindowManagerService服務的鏈接,另外一方面是從WindowManagerService服務到Activity組件的鏈接。從Activity組件到WindowManagerService服務的鏈接是以Activity組件所在的應用程序進程爲單位來進行的。當一個應用程序進程在啓動第一個Activity組件的時候,它便會打開一個到WindowManagerService服務的鏈接,這個鏈接以應用程序進程從WindowManagerService服務處得到一個實現了IWindowSession接口的Session代理對象來標誌。從WindowManagerService服務到Activity組件的鏈接是以Activity組件爲單位來進行的。在應用程序進程這一側,每個Activity組件都關聯一個實現了IWindow接口的W對象,這個W對象在Activity組件的視圖對象建立完成以後,就會經過前面所得到一個Session代理對象來傳遞給WindowManagerService服務,而WindowManagerService服務接收到這個W對象以後,就會在內部建立一個WindowState對象來描述與該W對象所關聯的Activity組件的窗口狀態,而且之後就經過這個W對象來控制對應的Activity組件的窗口狀態。android
上述Activity組件與WindowManagerService服務之間的鏈接模型如圖1所示:windows
圖1 Activity組件與WindowManagerService服務之間的鏈接模型數組
從圖1還能夠看出,每個Activity組件在ActivityManagerService服務內部,都對應有一個ActivityRecord對象,這個ActivityRecord對象是Activity組件啓動的過程當中建立的,用來描述Activity組件的運行狀態,這一點能夠參考前面Android應用程序啓動過程源代碼分析一文。這樣,每個Activity組件在應用程序進程、WindowManagerService服務和ActivityManagerService服務三者之間就分別一一地創建了鏈接。在本文中,咱們主要關注Activity組件在應用程序進程和WindowManagerService服務之間以及在WindowManagerService服務和ActivityManagerService服務之間的鏈接。安全
接下來咱們就經過Session類、W類和WindowState類的實現來簡要描述Activity組件與WindowManagerService服務之間的鏈接,如圖2和圖3所示:session
圖2 W類的實現app
圖3 Session類和WindowState類的實現框架
W類實現了IWindow接口,它的類實例是一個Binder本地對象。從前面Android應用程序窗口(Activity)的視圖對象(View)的建立過程分析一文能夠知道,一個Activity組件在啓動的過程當中,會建立一個關聯的ViewRoot對象,用來配合WindowManagerService服務來管理該Activity組件的窗口狀態。在這個ViewRoot對象內部,有一個類型爲W的成員變量mWindow,它是在ViewRoot對象的建立過程當中建立的。ide
ViewRoot類有一個靜態成員變量sWindowSession,它指向了一個實現了IWindowSession接口的Session代理對象。當應用程序進程啓動第一個Activity組件的時候,它就會請求WindowManagerService服務發送一個創建鏈接的Binder進程間通訊請求。WindowManagerService服務接收到這個請求以後,就會在內部建立一個類型爲Session的Binder本地對象,而且將這個Binder本地對象返回給應用程序進程,後者因而就會獲得一個Session代理對象,而且保存在ViewRoot類的靜態成員變量sWindowSession中。svg
有了這個Session代理對象以後,應用程序進程就能夠在啓動Activity組件的時候,調用它的成員函數add來將與該Activity組件所關聯的一個W對象傳遞給WindowManagerService服務,後者因而就會獲得一個W代理對象,而且會以這個W代理對象來建立一個WindowState對象,即將這個W代理對象保存在新建立的WindowState對象的成員變量mClient中。這個WindowState對象的其他成員變量的描述能夠參考前面Android應用程序窗口(Activity)實現框架簡要介紹和學習計劃一文的圖7,這裏再也不詳述。
Session類的描述一樣能夠參考前面Android應用程序窗口(Activity)實現框架簡要介紹和學習計劃一文,這裏咱們主要描述一下它的做用。從圖3能夠看出,Session類實現了IWindowSession接口,所以,應用程序進程就能夠經過保存在ViewRoot類的靜態成員變量sWindowSession所描述的一個Session代理對象所實現的IWindowSession接口來與WindowManagerService服務通訊,例如:
1. 在Activity組件的啓動過程當中,調用這個IWindowSession接口的成員函數add能夠將一個關聯的W對象傳遞到WindowManagerService服務,以便WindowManagerService服務能夠爲該Activity組件建立一個WindowState對象。
2. 在Activity組件的銷燬過程當中,調用這個這個IWindowSession接口的成員函數remove來請求WindowManagerService服務以前爲該Activity組件所建立的一個WindowState對象,這一點能夠參考前面Android應用程序鍵盤(Keyboard)消息處理機制分析一文的鍵盤消息接收通道註銷過程分析。
3. 在Activity組件的運行過程當中,調用這個這個IWindowSession接口的成員函數relayout來請求WindowManagerService服務來對該Activity組件的UI進行佈局,以便該Activity組件的UI能夠正確地顯示在屏幕中。
咱們再來看W類的做用。從圖2能夠看出,W類實現了IWindow接口,所以,WindowManagerService服務就能夠經過它在內部所建立的WindowState對象的成員變量mClient來要求運行在應用程序進程這一側的Activity組件來配合管理窗口的狀態,例如:
1. 當一個Activity組件的窗口的大小發生改變後,WindowManagerService服務就會調用這個IWindow接口的成員函數resized來通知該Activity組件,它的大小發生改變了。
2. 當一個Activity組件的窗口的可見性以後,WindowManagerService服務就會調用這個IWindow接口的成員函數dispatchAppVisibility來通知該Activity組件,它的可見性發生改變了。
3. 當一個Activity組件的窗口得到或者失去焦點以後,WindowManagerService服務就會調用這個IWindow接口的成員函數windowFoucusChanged來通知該Activity組件,它的焦點發生改變了。
理解了Activity組件在應用程序進程和WindowManagerService服務之間的鏈接模型以後,接下來咱們再經過簡要分析Activity組件在WindowManagerService服務和ActivityManagerService服務之間的鏈接。
Activity組件在WindowManagerService服務和ActivityManagerService服務之間的鏈接是經過一個AppWindowToken對象來描述的。AppWindowToken類的實現如圖4所示:
圖4 AppWindowToken類的實現
每個Activity組件在啓動的時候,ActivityManagerService服務都會內部爲該Activity組件建立一個ActivityRecord對象,而且會以這個ActivityRecord對象所實現的一個IApplicationToken接口爲參數,請求WindowManagerService服務爲該Activity組件建立一個AppWindowToken對象,即將這個IApplicationToken接口保存在新建立的AppWindowToken對象的成員變量appToken中。同時,這個ActivityRecord對象還會傳遞給它所描述的Activity組件所運行在應用程序進程,因而,應用程序進程就能夠在啓動完成該Activity組件以後,將這個ActivityRecord對象以及一個對應的W對象傳遞給WindowManagerService服務,後者接着就會作兩件事情:
1. 根據得到的ActivityRecord對象的IApplicationToken接口來找到與之對應的一個AppWindowToken對象;
2. 根據得到的AppWindowToken對象以及前面傳遞過來的W代理對象來爲正在啓動的Activity組件建立一個WindowState對象,而且將該AppWindowToken對象保存在新建立的WindowState對象的成員變量mAppToken中。
順便提一下,AppWindowToken類是從WindowToken類繼續下來的。WindowToken類也是用來標誌一個窗口的,不過這個窗口類型除了是應用程序窗口,即Activity組件窗口以外,還能夠是其它的,例如,輸入法窗口或者壁紙窗口類型等,而AppWindowToken類只是用來描述Activity組件窗口。當WindowToken類是用來描述Activity組件窗口的時候,它的成員變量token指向的就是用來描述該Activity組件的一個ActivityRecord對象所實現的一個IBinder接口,而成員變量appWindowToken指向的就是其子類AppWindowToken對象。當另外一方面,當WindowToken類是用來描述非Activity組件窗口的時候,它的成員變量appWindowToken的值就會等於null。這樣,咱們就能夠經過WindowToken類的成員變量appWindowToken的值來判斷一個WindowToken對象是不是用來描述一個Activity組件窗口的,便是否是用來描述一個應用程序窗口的。
上面所描述的Activity組件在ActivityManagerService服務和WindowManagerService服務之間以及應用程序進程和WindowManagerService服務之間的鏈接模型比較抽象,接下來,咱們再經過三個過程來分析它們彼此之間的鏈接模型,以下所示:
1. ActivityManagerService服務請求WindowManagerService服務爲一個Activity組件建立一個AppWindowToken對象的過程;
2. 應用程序進程請求WindowManagerService服務建立一個Session對象的過程;
3. 應用程序進程請求WindowManagerService服務爲一個Activity組件建立一個WindowState對象的過程。
經過這三個過程的分析,咱們就能夠對應用程序進程、ActivityManagerService服務和WindowManagerService服務的關係有一個深入的認識了。
一. AppWindowToken對象的建立過程
從前面Android應用程序啓動過程源代碼分析一文的Step 9能夠知道,Activity組件在啓動的過程當中,會調用到ActivityStack類的成員函數startActivityLocked,該函數會請求WindowManagerService服務爲當前正在啓動的Activity組件建立一個AppWindowToken對象。接下來,咱們就從ActivityStack類的成員函數startActivityLocked開始分析一個AppWindowToken對象的建立過程,如圖5所示:
圖4 AppWindowToken對象的建立過程
這個過程能夠分爲3步,接下來咱們就詳細分析每個步驟。
Step 1. ActivityStack.startActivityLocked
public class ActivityStack { ...... final ActivityManagerService mService; ...... final ArrayList mHistory = new ArrayList(); ...... private final void startActivityLocked(ActivityRecord r, boolean newTask, boolean doResume) { final int NH = mHistory.size(); int addPos = -1; if (!newTask) { // If starting in an existing task, find where that is... boolean startIt = true; for (int i = NH-1; i >= 0; i--) { ActivityRecord p = (ActivityRecord)mHistory.get(i); if (p.finishing) { continue; } if (p.task == r.task) { // Here it is! Now, if this is not yet visible to the // user, then just add it without starting; it will // get started when the user navigates back to it. addPos = i+1; if (!startIt) { mHistory.add(addPos, r); ...... mService.mWindowManager.addAppToken(addPos, r, r.task.taskId, r.info.screenOrientation, r.fullscreen); ...... return; } break; } if (p.fullscreen) { startIt = false; } } } // Place a new activity at top of stack, so it is next to interact // with the user. if (addPos < 0) { addPos = NH; } ...... // Slot the activity into the history stack and proceed mHistory.add(addPos, r); ...... if (NH > 0) { ...... mService.mWindowManager.addAppToken( addPos, r, r.task.taskId, r.info.screenOrientation, r.fullscreen); ..... } else { // If this is the first activity, don't do any fancy animations, // because there is nothing for it to animate on top of. mService.mWindowManager.addAppToken(addPos, r, r.task.taskId, r.info.screenOrientation, r.fullscreen); } ...... if (doResume) { resumeTopActivityLocked(null); } } ...... }
這個函數定義在文件frameworks/base/services/java/com/android/server/am/ActivityStack.java中。
參數r是一個ActivityRecord對象,用來描述的是要啓動的Activity組件,參數newTask是一個布爾變量,用來描述要啓動的Activity組件是不是在新任務中啓動的,參數doResume也是一個布爾變量,用來描述是否須要立刻將Activity組件啓動起來。
ActivityStack類的成員變量mService指向的是系統的ActivityManagerService,另一個成員變量mHistory是一個數組列表,用來描述系統的Activity組件堆棧。
當參數newTask的值等於false時,就說明參數r所描述的Activity組件是在已有的一個任務中啓動的,所以,這時候ActivityStack類的成員函數startActivityLocked就會從上到下遍歷保存成員變量mHistory,找到一個已有的Activity組件,它與參數r所描述的Activity組件是屬於同一個任務的,即它們的成員變量task的值相等。找到這樣的一個Activity組件以後,若是位於它上面的其它Activity組件的窗口至少有一個全屏的,即變量startIt的值等於true,那麼ActivityStack類的成員函數startActivityLocked就只是將參數r所描述的Activity組件加入到成員變量mHistory所描述的一個Activity組件堆棧中,以及調用成員變量mService所描述的ActivityManagerService服務的成員變量mWindowManager所描述的WindowManagerService服務的成員函數addAppToken來爲它建立一個AppWindowToken對象,而後就返回了,即不會執行下面的代碼來啓動參數r所描述的Activity組件。這至關因而延遲到參數r所描述的Activity組件可見時,纔將它啓動起來。
當參數newTask的值等於true時,就說明參數r所描述的Activity組件是在一個任務中啓動的,這時候ActivityStack類的成員函數startActivityLocked就會首先將它添加到成員變量mHistory所描述的一個Activity組件堆棧,接着再判斷它是不是系統中第一個啓動的Activity組件。若是是系統中第一個啓動的Activity組件,那麼ActivityStack類的成員函數startActivityLocked就只是簡單地調用WindowManagerService服務的成員函數addAppToken來爲它建立一個AppWindowToken對象就完事了。若是不是系統系統中第一個啓動的Activity組件,那麼ActivityStack類的成員函數startActivityLocked除了會調用WindowManagerService服務的成員函數addAppToken來爲它建立一個AppWindowToken對象以外,還會爲它建立一些啓動動畫等,咱們忽略這些代碼。
從上面的分析就能夠看出,不管如何,ActivityStack類的成員函數startActivityLocked都會調用WindowManagerService服務的成員函數addAppToken爲正在啓動的Activity組件建立一個AppWindowToken對象。建立完成這個AppWindowToken對象以後,若是參數doResume的值等於true,那麼ActivityStack類的成員函數startActivityLocked就會繼續調用另一個成員函數resumeTopActivityLocked來繼續執行啓動參數r所描述的一個Activity組件,這一步能夠參考前面Android應用程序啓動過程源代碼分析一文的Step 10。
接下來,咱們就繼續分析WindowManagerService類的成員函數addAppToken的實現,以即可以瞭解WindowManagerService服務是如何爲一個Activity組件建立一個AppWindowToken對象的。
Step 2. WindowManagerService.addAppToken
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... /** * Mapping from a token IBinder to a WindowToken object. */ final HashMap<IBinder, WindowToken> mTokenMap = new HashMap<IBinder, WindowToken>(); /** * The same tokens as mTokenMap, stored in a list for efficient iteration * over them. */ final ArrayList<WindowToken> mTokenList = new ArrayList<WindowToken>(); ...... /** * Z-ordered (bottom-most first) list of all application tokens, for * controlling the ordering of windows in different applications. This * contains WindowToken objects. */ final ArrayList<AppWindowToken> mAppTokens = new ArrayList<AppWindowToken>(); ...... public void addAppToken(int addPos, IApplicationToken token, int groupId, int requestedOrientation, boolean fullscreen) { ...... long inputDispatchingTimeoutNanos; try { inputDispatchingTimeoutNanos = token.getKeyDispatchingTimeout() * 1000000L; } catch (RemoteException ex) { ...... inputDispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; } synchronized(mWindowMap) { AppWindowToken wtoken = findAppWindowToken(token.asBinder()); if (wtoken != null) { ...... return; } wtoken = new AppWindowToken(token); wtoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos; wtoken.groupId = groupId; wtoken.appFullscreen = fullscreen; wtoken.requestedOrientation = requestedOrientation; mAppTokens.add(addPos, wtoken); ...... mTokenMap.put(token.asBinder(), wtoken); mTokenList.add(wtoken); // Application tokens start out hidden. wtoken.hidden = true; wtoken.hiddenRequested = true; //dump(); } } ...... }
這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
WindowManagerService類有三個成員變量mTokenMap、mTokenList和mAppTokens,它們都是用來描述系統中的窗口的。
成員變量mTokenMap指向的是一個HashMap,它裏面保存的是一系列的WindowToken對象,每個WindowToken對象都是用來描述一個窗口的,而且是以描述這些窗口的一個Binder對象的IBinder接口爲鍵值的。例如,對於Activity組件類型的窗口來講,它們分別是以用來描述它們的一個ActivityRecord對象的IBinder接口保存在成員變量mTokenMap所指向的一個HashMap中的。
成員變量mTokenList指向的是一個ArrayList,它裏面保存的也是一系列WindowToken對象,這些WindowToken對象與保存在成員變量mTokenMap所指向的一個HashMap中的WindowToken對象是同樣的。成員變量mTokenMap和成員變量mTokenList的區別就在於,前者在給定一個IBinder接口的狀況下,能夠迅速指出是否存在一個對應的窗口,然後者能夠迅速遍歷系統中的窗口。
成員變量mAppTokens指向的也是一個ArrayList,不過它裏面保存的是一系列AppWindowToken對象,每個AppWindowToken對象都是用來描述一個Activity組件的窗口的,而這些AppWindowToken對象是以它們描述的窗口的Z軸座標由小到大保存在這個ArrayList中的,這樣咱們就能夠經過這個ArrayList來從上到下或者從下到上地遍歷系統中的全部Activity組件窗口。因爲這些AppWindowToken對象所描述的Activity組件窗口也是一個窗口,而且AppWindowToken類是從WindowToken繼承下來的,所以,這些AppWindowToken對象還會同時被保存在成員變量mTokenMap所指向的一個HashMap和成員變量mTokenList所指向的一個ArrayList中。
理解了WindowManagerService類的成員變量mTokenMap、mTokenList和mAppTokens的做用以後,WindowManagerService類的成員函數addAppToken的實現就容易理解了。因爲參數token描述的是一個Activity組件窗口,所以,函數就會爲它建立一個AppWindowToken對象,而且將這個AppWindowToken對象分別保存在 WindowManagerService類的三個成員變量mTokenMap、mTokenList和mAppTokens中。不過在建立對象,首先會檢查與參數token所對應的AppWindowToken對象已經存在。若是已經存在,就什麼也不作就返回了。注意,參數addPos用來指定參數token描述的是一個Activity組件在系統Activity組件堆棧中的位置,這個位置同時也指定了爲該Activity組件在成員變量成員變量mAppTokens所指向的一個ArrayList中的位置。因爲保存在系統Activity組件堆棧的Activity組件原本就是按照它們的Z軸座標從小到大的順序來排列的,所以,保存在成員變量mAppTokens所指向的一個ArrayList中的AppWindowToken對象也是按照它們的Z軸座標從小到大的順序來排列的。
函數在爲參數token所描述一個Activity組件窗口建立了一個AppWindowToken對象以後,還會初始化它的一系列成員變量,這些成員變量的含義以下所示:
1. inputDispatchingTimeoutNanos,表示Activity組件窗口收到了一個IO輸入事件以後,若是沒有在規定的時間內處理完成該事件,那麼系統就認爲超時。這個超時值能夠由Activity組件自己來指定,便可以經過調用一個對應的ActivityRecord對象的成員函數getKeyDispatchingTimeout來得到。假如Activity組件沒有指定的話,那麼就會使用默認值DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS,即5000 * 1000000納秒。
2. groupId,表示Activity組件所屬的任務的ID。從前面Android應用程序啓動過程源代碼分析一文能夠知道,每個Activity組件都是屬於某一個任務的,而每個任務都用來描述一組相關的Activity組件的,這些Activity組件用來完成用戶的某一個操做。
3. appFullscreen,表示Activity組件的窗口是不是全屏的。若是一個Activity組件的窗口是全屏的,那麼它就會將位於它下面的全部窗口都擋住,這樣就能夠在渲染系統UI時進行優化,即不用渲染位於全屏窗口如下的其它窗口。
4. requestedOrientation,表示Activity組件所請求的方向。這個方向能夠是橫的(LANDSCAPE),也能夠是豎的(PORTRAIT)。
5. hidden,表示Activity組件是不是處於不可見狀態。
6. hiddenRequested,與hidden差很少,也是表示Activity組件是不是處於不可見狀態。二者的區別在於,在設置目標Activity組件的可見狀態時,若是系統等待執行Activity組件切換操做,那麼目標Activity組件的可見狀態不會立刻被設置,即它的hidden值不會立刻被設置,而只是設置它的hiddenRequested值,表示它的可見性狀態正在等待被設置。等到系統執行完成Activity組件切換操做以後,二者的值就會一致了。
接下來,咱們繼續分析一個AppWindowToken對象的建立過程,它AppWindowToken類的構造函數的實現。
Step 3. new AppWindowToken
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... class WindowToken { // The actual token. final IBinder token; // The type of window this token is for, as per WindowManager.LayoutParams. final int windowType; // Set if this token was explicitly added by a client, so should // not be removed when all windows are removed. final boolean explicit; ...... // If this is an AppWindowToken, this is non-null. AppWindowToken appWindowToken; ...... WindowToken(IBinder _token, int type, boolean _explicit) { token = _token; windowType = type; explicit = _explicit; } ...... } class AppWindowToken extends WindowToken { // Non-null only for application tokens. final IApplicationToken appToken; ...... AppWindowToken(IApplicationToken _token) { super(_token.asBinder(), WindowManager.LayoutParams.TYPE_APPLICATION, true); appWindowToken = this; appToken = _token; } ...... } ...... }
這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
AppWindowToken類的構造函數首先調用父類WindowToken的構造函數來執行父類的初始化工做,而後從父類WindowToken繼承下來的成員變量appWindowToken以及本身的成員變量appToken的值。參數_token指向的是一個ActivityRecord對象的IBinder接口,所以,AppWindowToken類的成員變量appToken描述的就是一個ActivityRecord對象。
WindowToken類的構造函數用來初始化token、windowType和explicit這三個成員變量。在咱們這個場景中,成員變量token指向的也是一個ActivityRecord對象的IBinder接口,用來標誌一個Activity組件的窗口,成員變量windowType用來描述窗口的類型,它的值等於WindowManager.LayoutParams.TYPE_APPLICATION,表示這是一個Activity組件窗口,成員變量explicit用來表示窗口是不是由應用程序進程請求添加的。
注意,當一個WindowToken對象的成員變量appWindowToken的值不等於null時,就表示它實際描述的是Activity組件窗口,而且這個成員變量指向的就是與該Activity組件所關聯的一個AppWindowToken對象。
至此,咱們就分析完成一個AppWindowToken對象的建立過程了,經過這個過程咱們就能夠知道,每個Activity組件,在ActivityManagerService服務內部都有一個對應的ActivityRecord對象,而且在WindowManagerService服務內部關聯有一個AppWindowToken對象。
二. Session對象的建立過程
應用程序進程在啓動第一個Activity組件的時候,就會請求與WindowManagerService服務創建一個鏈接,以即可以配合WindowManagerService服務來管理系統中的全部窗口。具體來講,就是應用程序進程在爲它裏面啓動的第一個Activity組件的視圖對象建立一個關聯的ViewRoot對象的時候,就會向WindowManagerService服務請求返回一個類型爲Session的Binder本地對象,這樣應用程序進程就能夠得到一個類型爲Session的Binder代理對象,之後就能夠經過這個Binder代理對象來和WindowManagerService服務進行通訊了。
從前面Android應用程序窗口(Activity)的視圖對象(View)的建立過程分析一文能夠知道,應用程序進程在爲它裏面啓動的Activity組件的視圖對象建立關聯ViewRoot對象是經過調用WindowManagerImpl類的成員函數addView來實現的,所以,接下來咱們就從WindowManagerImpl類的成員函數addView開始分析應用程序進程與WindowManagerService服務的鏈接過程,即一個Session對象的建立過程,如圖5所示:
圖5 Session對象的建立過程
這個過程能夠分爲5個步驟,接下來咱們就詳細分析每個步驟。
Step 1. WindowManagerImpl.addView
public class WindowManagerImpl implements WindowManager { ...... private void addView(View view, ViewGroup.LayoutParams params, boolean nest) { ...... ViewRoot root; ...... synchronized (this) { ...... root = new ViewRoot(view.getContext()); ...... } // do this last because it fires off messages to start doing things root.setView(view, wparams, panelParentView); } ...... }
這個函數定義在文件frameworks/base/core/java/android/view/WindowManagerImpl.java中。
這裏的參數view即爲正在啓動的Activity組件的視圖對象,WindowManagerImpl類的成員函數addView的目標就是爲它建立一個ViewRoot對象。這裏咱們只關注這個ViewRoot對象的建立過程,即ViewRoot類的構造函數的實現,WindowManagerImpl類的成員函數addView的詳細實現能夠參考前面Android應用程序窗口(Activity)的視圖對象(View)的建立過程分析一文的Step 12。
Step 2. new ViewRoot
public final class ViewRoot extends Handler implements ViewParent, View.AttachInfo.Callbacks { ...... public ViewRoot(Context context) { super(); ...... // Initialize the statics when this class is first instantiated. This is // done here instead of in the static block because Zygote does not // allow the spawning of threads. getWindowSession(context.getMainLooper()); ...... } ...... }
這個函數定義在文件frameworks/base/core/java/android/view/ViewRoot.java文件中。
ViewRoot類的構造函數是經過調用靜態成員函數getWindowSession來請求WindowManagerService服務爲應用程序進程建立一個返回一個類型爲Session的Binder本地對象的,所以,接下來咱們就繼續分析ViewRoot類的靜態成員函數getWindowSession的實現。
Step 3. ViewRoot.getWindowSession
public final class ViewRoot extends Handler implements ViewParent, View.AttachInfo.Callbacks { ...... static IWindowSession sWindowSession; static final Object mStaticInit = new Object(); static boolean mInitialized = false; ...... public static IWindowSession getWindowSession(Looper mainLooper) { synchronized (mStaticInit) { if (!mInitialized) { try { InputMethodManager imm = InputMethodManager.getInstance(mainLooper); sWindowSession = IWindowManager.Stub.asInterface( ServiceManager.getService("window")) .openSession(imm.getClient(), imm.getInputContext()); mInitialized = true; } catch (RemoteException e) { } } return sWindowSession; } } ...... }
這個函數定義在文件frameworks/base/core/java/android/view/ViewRoot.java文件中。
ViewRoot類的靜態成員函數getWindowSession首先得到得到應用程序所使用的輸入法管理器,接着再得到系統中的WindowManagerService服務的一個Binder代理對象。有了這個Binder代理對象以後,就能夠調用它的成員函數openSession來請求WindowManagerService服務返回一個類型爲Session的Binder本地對象。這個Binder本地對象返回來以後,就變成了一個類型爲Session的Binder代理代象,即一個實現了IWindowSession接口的Binder代理代象,而且保存在ViewRoot類的靜態成員變量sWindowSession中。在請求WindowManagerService服務返回一個類型爲Session的Binder本地對象的時候,應用程序進程傳遞給WindowManagerService服務的參數有兩個,一個是實現IInputMethodClient接口的輸入法客戶端對象,另一個是實現了IInputContext接口的一個輸入法上下文對象,它們分別是經過調用前面所得到的一個輸入法管理器的成員函數getClient和getInputContext來得到的。
從這裏就能夠看出,只有當ViewRoot類的靜態成員函數getWindowSession第一次被調用的時候,應用程序進程纔會請求WindowManagerService服務返回一個類型爲Session的Binder本地對象。又因爲ViewRoot類的靜態成員函數getWindowSession第一次被調用的時候,正好是處於應用程序進程中的第一個Activity組件啓動的過程當中,所以,應用程序進程是在啓動第一個Activity組件的時候,纔會請求與WindowManagerService服務創建鏈接。一旦鏈接創建起來以後,ViewRoot類的靜態成員變量mInitialized的值就會等於true,而且另一個靜態成員變量sWindowSession的值不等於null。同時,ViewRoot類的靜態成員函數getWindowSession是線程安全的,這樣就能夠避免多個線程同時調用它來重複請求WindowManagerService服務爲當前應用程序進程建立鏈接。
接下來,咱們就繼續分析求WindowManagerService類的成員函數openSession的實現,以即可以瞭解一個Session對象的建立過程。
Step 4. indowManagerService.openSession
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... public IWindowSession openSession(IInputMethodClient client, IInputContext inputContext) { if (client == null) throw new IllegalArgumentException("null client"); if (inputContext == null) throw new IllegalArgumentException("null inputContext"); Session session = new Session(client, inputContext); return session; } ...... }
這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
WindowManagerService類的成員函數openSession的實現很簡單,它以應用程序進程傳遞過來的一個輸入法客戶端對象和一個輸入法上下文對象來參數,來建立一個類型爲Session的Binder本地對象,而且將這個類型爲Session的Binder本地對象返回給應用程序進程。
接下來咱們就繼續分析Session類的構造函數的實現,以即可以瞭解一個Session對象的建立過程。
Step 5. new Session
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... final boolean mHaveInputMethods; ...... IInputMethodManager mInputMethodManager; ...... private final class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { final IInputMethodClient mClient; final IInputContext mInputContext; ...... public Session(IInputMethodClient client, IInputContext inputContext) { mClient = client; mInputContext = inputContext; ...... synchronized (mWindowMap) { if (mInputMethodManager == null && mHaveInputMethods) { IBinder b = ServiceManager.getService( Context.INPUT_METHOD_SERVICE); mInputMethodManager = IInputMethodManager.Stub.asInterface(b); } } long ident = Binder.clearCallingIdentity(); try { // Note: it is safe to call in to the input method manager // here because we are not holding our lock. if (mInputMethodManager != null) { mInputMethodManager.addClient(client, inputContext, mUid, mPid); } else { client.setUsingInputMethod(false); } client.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { ...... } finally { Binder.restoreCallingIdentity(ident); } } ...... } ...... }
這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
Session類有兩個成員變量mClient和mInputContext,分別用來保存在從應用程序進程傳遞過來的一個輸入法客戶端對象和一個輸入法上下文對象,它們是在Session類的構造函數中初始化的。
Session類的構造函數還會檢查WindowManagerService服務是否須要得到系統中的輸入法管理器服務,即檢查WindowManagerService類的成員變量mHaveInputMethods的值是否等於true。若是這個值等於true,而且WindowManagerService服務尚未得到系統中的輸入法管理器服務,即WindowManagerService類的成員變量mInputMethodManager的值等於null,那麼Session類的構造函數就會首先得到這個輸入法管理器服務,而且保存在WindowManagerService類的成員變量mInputMethodManager中。
得到了系統中的輸入法管理器服務以後,Session類的構造函數就能夠調用它的成員函數addClient來爲正在請求與WindowManagerService服務創建鏈接的應用程序進程增長它所使用的輸入法客戶端對象和輸入法上下文對象了。
至此,咱們就分析完成一個Session對象的建立過程了,經過這個過程咱們就能夠知道,每個應用程序進程在WindowManagerService服務內部都有一個類型爲Session的Binder本地對象,用來它與WindowManagerService服務之間的鏈接,而有了這個鏈接以後,WindowManagerService服務就能夠請求應用進程配合管理系統中的應用程序窗口了。
三. WindowState對象的建立過程
在Android系統中,WindowManagerService服務負責統一管理系統中的全部窗口,所以,當運行在應用程序進程這一側的Activity組件在啓動完成以後,須要與WindowManagerService服務創建一個鏈接,以便WindowManagerService服務能夠管理它的窗口。具體來講,就是應用程序進程將一個Activitty組件的視圖對象設置到與它所關聯的一個ViewRoot對象的內部的時候,就會將一個實現了IWindow接口的Binder本地對象傳遞WindowManagerService服務。這個實現了IWindow接口的Binder本地對象惟一地標識了一個Activity組件,WindowManagerService服務接收到了這個Binder本地對象以後,就會將它保存在一個新建立的WindowState對象的內部,這樣WindowManagerService服務之後就能夠經過它來和Activity組件通訊,以即可以要求Activity組件配合來管理系系統中的全部窗口。
從前面Android應用程序窗口(Activity)的視圖對象(View)的建立過程分析一文能夠知道,應用程序進程將一個Activity組件的視圖對象設置到與它所關聯的一個ViewRoot對象的內部是經過調用ViewRoot類的成員函數setView來實現的,所以,接下來咱們就從ViewRoot類的成員函數setView開始分析Activity組件與WindowManagerService服務的鏈接過程,即一個WindowState對象的建立過程,如圖6所示:
圖6 WindowState對象的建立過程
這個過程能夠分爲7個步驟,接下來咱們就詳細分析每個步驟。
Step 1. ViewRoot.setView
public final class ViewRoot extends Handler implements ViewParent, View.AttachInfo.Callbacks { ...... static IWindowSession sWindowSession; ...... final W mWindow; View mView; ...... public ViewRoot(Context context) { super(); ...... mWindow = new W(this, context); ...... } ...... public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; ...... try { res = sWindowSession.add(mWindow, mWindowAttributes, getHostVisibility(), mAttachInfo.mContentInsets, mInputChannel); } catch (RemoteException e) { ...... } finally { ...... } ...... } ...... } } ...... }
這個函數定義在文件frameworks/base/core/java/android/view/ViewRoot.java文件中。
這裏的參數view即爲正在啓動的Activity組件的視圖對象,ViewRoot類的成員函數setView會將它保存成員變量mView中,這樣就能夠將一個Activity組件的視圖對象和一個ViewRoot對象關聯起來。ViewRoot類的成員函數setView接下來還會調用靜態成員變量sWindowSession所描述的一個實現了IWindowSession接口的Binder代理對象的成員函數add來請求WindowManagerService服務爲正在啓動的Activity組件建立一個WindowState對象。接下來咱們就主要關注WindowState對象的建立過程,ViewRoot類的成員函數setView的詳細實現能夠參考前面Android應用程序窗口(Activity)的視圖對象(View)的建立過程分析一文的Step 13。
注意,ViewRoot類的成員函數setView在請求WindowManagerService服務爲正在啓動的Activity組件建立一個WindowState對象的時候,會傳遞一個類型爲W的Binder本地對象給WindowManagerService服務。這個類型爲W的Binder本地對象實現了IWindow接口,保存在ViewRoot類的成員變量mWindow中,它是在ViewRoot類的構造函數中建立的,之後WindowManagerService服務就會經過它來和Activity組件通訊。
從前面第二部的內容能夠知道,ViewRoot類的靜態成員變量sWindowSession所指向的一個Binder代理對象引用的是運行在WindowManagerService服務這一側的一個Session對象,所以,接下來咱們就繼續分析Session類的成員函數add的實現,以即可以瞭解一個WindowState對象的建立過程。
Step 2. Session.add
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private final class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { ...... public int add(IWindow window, WindowManager.LayoutParams attrs, int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) { return addWindow(this, window, attrs, viewVisibility, outContentInsets, outInputChannel); } ...... } ...... }
這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
Session類的成員函數add的實現很簡單,它只是調用了外部類WindowManagerService的成員函數addWindow來進一步爲正在啓動的Activity組件建立一個WindowState對象。
Step 3. WindowManagerService.addWindow
這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中,它的實現比較長,咱們分段來閱讀:
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... public int addWindow(Session session, IWindow client, WindowManager.LayoutParams attrs, int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) { ...... WindowState attachedWindow = null; WindowState win = null; synchronized(mWindowMap) { ...... if (mWindowMap.containsKey(client.asBinder())) { ...... return WindowManagerImpl.ADD_DUPLICATE_ADD; }
這段代碼首先在WindowManagerService類的成員變量mWindowMap所描述的一個HashMap中檢查是否存在一個與參數client所對應的WindowState對象,若是已經存在,那麼就說明WindowManagerService服務已經爲它建立過一個WindowState對象了,所以,這裏就不會繼續往前執行,而是直接返回一個錯誤碼WindowManagerImpl.ADD_DUPLICATE_ADD。
咱們繼續往前看代碼:
if (attrs.type >= FIRST_SUB_WINDOW && attrs.type <= LAST_SUB_WINDOW) { attachedWindow = windowForClientLocked(null, attrs.token, false); if (attachedWindow == null) { ...... return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN; } if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) { ...... return WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN; } }
參數attrs指向的是一個WindowManager.LayoutParams對象,用來描述正在啓動的Activity組件的UI佈局,當它的成員變量type的值大於等於FIRST_SUB_WINDOW而且小於等於LAST_SUB_WINDOW的時候,就說明如今要增長的是一個子窗口。在這種狀況下,就必需要指定一個父窗口,而這個父窗口是經過數attrs指向的是一個WindowManager.LayoutParams對象的成員變量token來指定的,所以,這段代碼就會調用WindowManagerService類的另一個成員函數windowForClientLocked來得到用來描述這個父窗口的一個WindowState對象,而且保存在變量attachedWindow。
若是獲得變量attachedWindow的值等於null,那麼就說明父窗口不存在,這是不容許的,所以,函數就不會繼續向前執行,而是直接返回一個錯誤碼WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN。另外一方面,若是變量attachedWindow的值不等於null,可是它的成員變量mAttrs所指向的一個WindowManager.LayoutParams對象的成員變量type的值也是大於等於FIRST_SUB_WINDOW而且小於等於LAST_SUB_WINDOW,那麼也說明找到的父窗口也是一個子窗口,這種狀況也是不容許的,所以,函數就不會繼續向前執行,而是直接返回一個錯誤碼WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN。
咱們繼續往前看代碼:
boolean addToken = false; WindowToken token = mTokenMap.get(attrs.token); if (token == null) { if (attrs.type >= FIRST_APPLICATION_WINDOW && attrs.type <= LAST_APPLICATION_WINDOW) { ...... return WindowManagerImpl.ADD_BAD_APP_TOKEN; } if (attrs.type == TYPE_INPUT_METHOD) { ...... return WindowManagerImpl.ADD_BAD_APP_TOKEN; } if (attrs.type == TYPE_WALLPAPER) { ...... return WindowManagerImpl.ADD_BAD_APP_TOKEN; } token = new WindowToken(attrs.token, -1, false); addToken = true; } else if (attrs.type >= FIRST_APPLICATION_WINDOW && attrs.type <= LAST_APPLICATION_WINDOW) { AppWindowToken atoken = token.appWindowToken; if (atoken == null) { ...... return WindowManagerImpl.ADD_NOT_APP_TOKEN; } else if (atoken.removed) { ...... return WindowManagerImpl.ADD_APP_EXITING; } if (attrs.type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) { // No need for this guy! ...... return WindowManagerImpl.ADD_STARTING_NOT_NEEDED; } } else if (attrs.type == TYPE_INPUT_METHOD) { if (token.windowType != TYPE_INPUT_METHOD) { ...... return WindowManagerImpl.ADD_BAD_APP_TOKEN; } } else if (attrs.type == TYPE_WALLPAPER) { if (token.windowType != TYPE_WALLPAPER) { ...... return WindowManagerImpl.ADD_BAD_APP_TOKEN; } }
這段代碼首先在WindowManagerService類的成員變量mTokenMap所描述的一個HashMap中檢查WindowManagerService服務是否已經爲正在增長的窗口建立過一個WindowToken對象了。
若是尚未建立過WindowToken對,即變量token的值等於null,那麼這段代碼就會進一步檢查正在增長的窗口的類型。若是正在增長的窗口是屬於應用程序窗口(FIRST_APPLICATION_WINDOW~LAST_APPLICATION_WINDOW,即Activity組件窗口),或者輸入法窗口(TYPE_INPUT_METHOD),或者壁紙窗口(TYPE_WALLPAPER),那麼這三種狀況都是不容許的,所以,函數就不會繼續向前執行,而是直接返回一個錯誤碼WindowManagerImpl.ADD_BAD_APP_TOKEN。例如,對於應用程序窗口來講,與它所對應的Activity組件是在以前就已經由ActivityManagerService服務請求WindowManagerService服務建立過一個AppWindowToken對象了的(AppWindowToken對象也是一個WindowToken對象,由於AppWindowToken類繼承了WindowToken類),這個過程能夠參考前面第一部分的內容。若是不是上述三種狀況,那麼這段代碼就會爲正在增長的窗口建立一個WindowToken對象,而且保存在變量token中。
若是已經建立過WindowToken對象,即變量token的值不等於null,那麼就說明正在增長的窗口或者是應用程序窗口,或者是輸入法窗口,或者是壁紙窗口。下面咱們就爲三種狀況來討論。
若是是應用程序窗口,從前面第一部分的內容能夠知道,WindowToken對象token的成員變量appWindowToken的值必須不能等於null,而且指向的是一個AppWindowToken對象。所以,當WindowToken對象token的成員變量appWindowToken的值等於null的時候,函數就不會繼續向前執行,而是直接返回一個錯誤碼WindowManagerImpl.ADD_NOT_APP_TOKEN。另外一方面,雖然WindowToken對象token的成員變量appWindowToken的值不等於null,可是它所指向的一個AppWindowToken對象的成員變量removed的值等於true時,那麼就表示對應的Activity組件已經被移除,在這種狀況下,函數也不會繼續向前執行,而是直接返回一個錯誤碼WindowManagerImpl.ADD_APP_EXITING。還有一種特殊的應用程序窗口,它的類型爲TYPE_APPLICATION_STARTING。這種類型的窗口稱爲起始窗口,它是在一個Activity組件的窗口顯示出來以前就顯示的。所以,若是當前正在增長的是一個超始窗口,而且它所附屬的應用程序窗口,即變量atoken所描述的應用程序窗口,已經顯示出來了,即變量atoken所指向的一個AppWindowToken對象的成員變量firstWindowDrawn的值等於true,那麼函數也不會繼續向前執行,而是直接返回一個錯誤碼WindowManagerImpl.ADD_STARTING_NOT_NEEDED。
若是是輸入法窗口,可是參數attrs所描述的一個WindowManager.LayoutParams對象的成員變量windowType的值不等於TYPE_INPUT_METHOD,那麼指定的窗口類型就是不匹配的。在這種狀況下,函數就不會繼續向前執行,而是直接返回一個錯誤碼WindowManagerImpl.ADD_BAD_APP_TOKEN。
若是是壁紙窗口,可是參數attrs所描述的一個WindowManager.LayoutParams對象的成員變量windowType的值不等於TYPE_WALLPAPER,那麼指定的窗口類型就是不匹配的。在這種狀況下,函數也不會繼續向前執行,而是直接返回一個錯誤碼WindowManagerImpl.ADD_BAD_APP_TOKEN。
咱們繼續往前看代碼:
win = new WindowState(session, client, token, attachedWindow, attrs, viewVisibility); ...... mPolicy.adjustWindowParamsLw(win.mAttrs); res = mPolicy.prepareAddWindowLw(win, attrs); if (res != WindowManagerImpl.ADD_OKAY) { return res; }
經過上面的合法性檢查以後,這裏就能夠爲正在增長的窗口建立一個WindowState對象了。
WindowManagerService類的成員變量mPolicy指向的是一個實現了WindowManagerPolicy接口的窗口管理策略器。在Phone平臺中,這個窗口管理策略器是由com.android.internal.policy.impl.PhoneWindowManager來實現的,它負責對系統中的窗口實現制定一些規則。這裏主要是調用窗口管理策略器的成員函數adjustWindowParamsLw來調整當前正在增長的窗口的佈局參數,以及調用成員函數prepareAddWindowLw來檢查當前應用程序進程請求增長的窗口是不是合法的。若是不是合法的,即變量res的值不等於WindowManagerImpl.ADD_OKAY,那麼函數就不會繼續向前執行,而直接返回錯誤碼res。
WindowState對象的建立過程咱們在接下來的Step 4中再分析,如今咱們繼續向前看代碼:
if (outInputChannel != null) { String name = win.makeInputChannelName(); InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); win.mInputChannel = inputChannels[0]; inputChannels[1].transferToBinderOutParameter(outInputChannel); mInputManager.registerInputChannel(win.mInputChannel); }
這段代碼是用建立IO輸入事件鏈接通道的,以便正在增長的窗口能夠接收到系統所發生的鍵盤和觸摸屏事件,能夠參考前面Android應用程序鍵盤(Keyboard)消息處理機制分析一文,這裏再也不詳述。
咱們繼續向前看代碼:
if (addToken) { mTokenMap.put(attrs.token, token); mTokenList.add(token); } win.attach(); mWindowMap.put(client.asBinder(), win); if (attrs.type == TYPE_APPLICATION_STARTING && token.appWindowToken != null) { token.appWindowToken.startingWindow = win; }
這段代碼首先檢查變量addToken的值是否等於true。若是等於true的話,那麼就說明變量token所指向的一個WindowToken對象是在前面新建立的。在這種狀況下,就須要將這個新建立的WindowToken對象分別添加到WindowManagerService類的成員變量mTokeMap和mTokenList分別描述的一個HashMap和一個ArrayList中去。
這段代碼接下來再調用前面所建立的一個WindowState對象win的成員函數attach來爲當前正在增長的窗口建立一個用來鏈接到SurfaceFlinger服務的SurfaceSession對象。有了這個SurfaceSession對象以後,當前正在增長的窗口就能夠和SurfaceFlinger服務通訊了。在接下來的Step 5中,咱們再詳細分析WindowState類的成員函數attach的實現。
這段代碼最後還作了兩件事情。第一件事情是將前面所建立的一個WindowState對象win添加到WindowManagerService類的成員變量mWindowMap所描述的一個HashMap中,這是以參數所描述的一個類型爲W的Binder代理對象的IBinder接口來鍵值來保存的。第二件事情是檢查當前正在增長的是不是一個起始窗口,若是是的話,那麼就會將前面所建立的一個WindowState對象win設置到用來描述它所屬的Activity組件的一個AppWindowToken對象的成員變量startingWindow中去,這樣系統就能夠在顯示這個Activity組件的窗口以前,先顯示該起始窗口。
咱們繼續向前看代碼:
boolean imMayMove = true; if (attrs.type == TYPE_INPUT_METHOD) { mInputMethodWindow = win; addInputMethodWindowToListLocked(win); imMayMove = false; } else if (attrs.type == TYPE_INPUT_METHOD_DIALOG) { mInputMethodDialogs.add(win); addWindowToListInOrderLocked(win, true); adjustInputMethodDialogsLocked(); imMayMove = false; } else { addWindowToListInOrderLocked(win, true); if (attrs.type == TYPE_WALLPAPER) { ...... adjustWallpaperWindowsLocked(); } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) { adjustWallpaperWindowsLocked(); } }
WindowManagerService類有一個成員變量mWindows,它是一個類型爲WindowState的ArrayList,保存在它裏面的WindowState對象所描述的窗口是按照它們的Z軸座標從小到大的順序來排列的。如今咱們須要考慮將用來描述當前正在增長的窗口的WindowState對象win添加到這個ArrayList的合適位置中去。咱們分三種狀況考慮。
若是當前正在增長的窗口是一個輸入法窗口,那麼WindowManagerService服務就須要按照Z軸座標從大到小的順序來檢查當前是哪個窗口是須要輸入法窗口的。找到了這個位於最上面的須要輸入法窗口的窗口以後,就能夠將輸入法窗口放置在它的上面了,便可以將WindowState對象win添加到WindowManagerService類的成員變量mWindows所描述的一個ArrayList的合適位置了。這兩個操做是經過調用WindowManagerService類的成員函數addInputMethodWindowToListLocked來實現的。同時,WindowManagerService類的成員變量mInputMethodWindow指向的是系統當前正在使用的輸入法窗口,所以,在這種狀況下,咱們還須要將WindowState對象win保存在它裏面。
若是當前正在添加的窗口是一個輸入法對話框,那麼WindowManagerService服務就須要作兩件事情。第一件事情是將WindowState對象win添加到WindowManagerService類的成員變量mWindows所描述的一個ArrayList中去,這是經過調用WindowManagerService類的成員函數addWindowToListInOrderLocked來實現的。第二件事情是調用WindowManagerService類的成員函數adjustInputMethodDialogsLocked來調整正在增長的輸入法對話框在WindowManagerService類的成員變量mWindows所描述的一個ArrayList中的位置,使得它位於輸入法窗口的上面。
在上述兩種狀況下,系統的輸入法窗口和輸入法對話框的位置都是獲得合適的調整的了,所以,這段代碼就會將變量imMayMove的值設置爲false,表示後面不須要再調整輸入法窗口和輸入法對話框的位置了。
若是當前正在添加的窗口是一個應用程序窗口或者壁紙窗口,那麼WindowManagerService服務都須要將WindowState對象win添加到WindowManagerService類的成員變量mWindows所描述的一個ArrayList中去。添加完成以後,若是發現當前添加的窗口是一個壁紙窗口或者當前添加的是一個須要顯示壁紙的應用程序窗口,那麼WindowManagerService服務還須要進一步調整系統中已經添加了的壁紙窗口的Z軸位置,使得它們位於最上面的須要顯示壁紙的窗口的下面,這是經過調用WindowManagerService類的成員函數adjustWallpaperWindowsLocked來實現的。
咱們繼續向前看代碼:
win.mEnterAnimationPending = true; mPolicy.getContentInsetHintLw(attrs, outContentInsets); if (mInTouchMode) { res |= WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE; } if (win == null || win.mAppToken == null || !win.mAppToken.clientHidden) { res |= WindowManagerImpl.ADD_FLAG_APP_VISIBLE; }
這段代碼作了四件事情。
第一件事情是將前面所建立的一個WindowState對象win的成員變量mEnterAnimationPending的值設置爲true,表示當前正在增長的窗口須要顯示一個進入動畫。
第二件事情是調用WindowManagerService類的成員變量mPolicy所描述的一個窗口管理策略器的成員函數getContentInsetHintLw來得到當前正在增長的窗口的UI內容邊襯大小,即當前正在增長的窗口能夠在屏幕中所得到的用來顯示UI內容的區域的大小,這一般是要排除屏幕邊框和狀態欄等所佔據的屏幕區域。
第三件事情是檢查WindowManagerService類的成員變量mInTouchMode的值是否等於true。若是等於true的話,那麼就說明系統運行在觸摸屏模式中,這時候這段代碼就會將返回值res的WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE位設置爲1。
第四件事情是檢查當前正在增長的窗口是不是處於可見的狀態。從第二個if語句能夠看出,因爲WindowState對象win的值在這裏不能夠等於null,所以,這裏只有兩種狀況下,前正在增長的窗口是處於可見狀態的。第一種狀況是WindowState對象的成員變量mAppToken的值等於null,這代表當前正在增長的窗口不是一個應用程序窗口,即不是一個Activity組件窗口,那麼它就有多是一個子窗口。因爲子窗口一般是在其父窗口處於可見的狀態下才會建立的,所以,這個子窗口就須要立刻顯示出來的,即須要將它的狀態設置爲可見的。第二種狀況是WindowState對象的成員變量mAppToken的值不等於null,這代表當前正在增長的窗口是一個應用程序窗口。在這種狀況下,WindowState對象的成員變量mAppToken指向的就是一個AppWindowToken對象。當這個AppWindowToken對象的成員變量clientHidden的值等於false的時候,就代表它所描述的一個Activity組件是處於可見狀態的,所以,這時候就須要將該Activity組件的窗口(即當前正在增長的窗口)的狀態設置爲可見的。在當前正在增長的窗口是處於可見狀態的狀況下,這段代碼就會將返回值res的WindowManagerImpl.ADD_FLAG_APP_VISIBLE位設置爲1。
咱們繼續向前看最後一段代碼:
boolean focusChanged = false; if (win.canReceiveKeys()) { focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS); if (focusChanged) { imMayMove = false; } } if (imMayMove) { moveInputMethodWindowsIfNeededLocked(false); } assignLayersLocked(); ...... if (focusChanged) { finishUpdateFocusedWindowAfterAssignLayersLocked(); } ...... } ... return res; } ...... }
最後一段代碼作了如下五件事情。
第一件事情是檢查當前正在增長的窗口是不是可以接收IO輸入事件的,即鍵盤輸入事件或者觸摸屏輸入事件。若是能夠的話,那麼就須要調用WindowManagerService類的成員函數updateFocusedWindowLocked來調整系統當前得到輸入焦點的窗口,由於當前正在增長的窗口可能會成爲新的能夠得到輸入焦點的窗口。若是WindowManagerService類的成員函數updateFocusedWindowLocked的返回值等於true,那麼就代表系統當前得到輸入焦點的窗口發生了變化。在這種狀況下,WindowManagerService類的成員函數updateFocusedWindowLocked也會順便調整輸入法窗口的位置,使得它位於系統當前得到輸入焦點的窗口的上面,所以,這時候這段代碼也會將變量imMayMove的值設置爲false,表示接下來不用調整輸入法窗口的位置了。
第二件事情是檢查變量imMayMove的值是否等於true。若是等於true的話,那麼就說明當前正在增長的窗口可能已經影響到系統的輸入法窗口的Z軸位置了,所以,這段代碼就須要調用WindowManagerService類的成員函數moveInputMethodWindowsIfNeededLocked來從新調整調整輸入法窗口的Z軸位置,使得它能夠位於最上面的那個須要輸入法窗口的窗口的上面。
第三件事情是調用WindowManagerService類的成員函數assignLayersLocked來調整系統中全部窗口的Z軸位置,這也是由於當前正在增長的窗口可能已經影響到系統中全部窗口的Z軸位置了。例如,假如當前增長的是一個正在啓動的Activity組件的窗口,那麼這個窗口的Z軸位置就應該是最大的,以即可以在最上面顯示。又如,假如當前增長的是一個子窗口,那麼這個子窗口就應該位於它的父窗口的上面。這些都要求從新調整系統中全部窗口的Z軸位置,以便每個窗口均可以在一個正確的位置上來顯示。
第四件事情檢查變量focusChanged的值是否等於true。若是等於true的話,那麼就說明系統中得到輸入焦點的窗口已經發生了變化。在這種狀況下,這段代碼就會調用WindowManagerService類的成員函數finishUpdateFocusedWindowAfterAssignLayersLocked來通知系統IO輸入管理器,新的得到焦點的窗口是哪個,以便系統IO輸入管理器接下來能夠將鍵盤輸入事件或者觸摸屏輸入事件分發給新的得到焦點的窗口來處理。WindowManagerService類的成員函數finishUpdateFocusedWindowAfterAssignLayersLocked的實現能夠參考前面Android應用程序鍵盤(Keyboard)消息處理機制分析一文,這裏再也不詳述。
第五件事情是變量res的值返回給應用程序進程,以便它能夠知道請求WindowManagerService服務增長一個窗口的執行狀況。
至此,WindowManagerService類的成員函數addWindow的實現就分析完成了,接下來咱們就繼續分析WindowState類的構造函數和成員函數attach的實現,以即可以瞭解一個WindowSate對象及其相關聯的一個SurfaceSession對象的建立過程。
Step 4. new WindowState
這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中,它的實現比較長,咱們分段來閱讀:
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private final class WindowState implements WindowManagerPolicy.WindowState { ...... WindowState(Session s, IWindow c, WindowToken token, WindowState attachedWindow, WindowManager.LayoutParams a, int viewVisibility) { mSession = s; mClient = c; mToken = token; mAttrs.copyFrom(a); mViewVisibility = viewVisibility; DeathRecipient deathRecipient = new DeathRecipient(); mAlpha = a.alpha;
這段代碼初始化WindowState類的如下六個成員變量:
-mSession:指向一個類型爲Session的Binder本地對象,使用參數s來初始化,表示當前所建立的WindowState對象是屬於哪個應用程序進程的。
-mClient:指向一個實現了IWindow接口的Binder代理對象,它引用了運行在應用程序進程這一側的一個類型爲W的Binder本地對象,使用參數c來初始化,經過它能夠與運行在應用程序進程這一側的Activity組件進行通訊。
-mToken:指向一個WindowToken對象,使用參數token來初始化,經過它就能夠知道惟一地標識一個窗口。
-mAttrs:指向一個WindowManager.LayoutParams對象,使用參數a來初始化,經過它就能夠知道當前當前所建立的WindowState對象所描述的窗口的佈局參數。
-mViewVisibility:這是一個整型變量,使用參數viewVisibility來初始化,表示當前所建立的WindowState對象所描述的窗口視圖的可見性。
-mAlpha:這是一個浮點數,使用參數a所描述的一WindowManager.LayoutParams對象的成員變量alpha來初始化,表示當前所建立的WindowState對象所描述的窗口的Alpha通道。
此外,這段代碼還建立了一個類型爲DeathRecipient的死亡通知接收者deathRecipient,它是用來監控參數c所引用的一個類型爲W的Binder本地對象的生命週期的。當這個Binder本地對象死亡的時候,就意味着當前所建立的WindowState對象所描述的窗口所在的應用程序進程已經退出了。接下來的這段代碼就是用來註冊死亡通知接收者deathRecipient的,以下所示:
try { c.asBinder().linkToDeath(deathRecipient, 0); } catch (RemoteException e) { mDeathRecipient = null; mAttachedWindow = null; mLayoutAttached = false; mIsImWindow = false; mIsWallpaper = false; mIsFloatingLayer = false; mBaseLayer = 0; mSubLayer = 0; return; } mDeathRecipient = deathRecipient;
註冊完成以後,前面所建立的死亡通知接收者deathRecipient就會保存在WindowState類的成員變量mDeathRecipientk 。
咱們繼續向前看代碼:
if ((mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW)) { // The multiplier here is to reserve space for multiple // windows in the same type layer. mBaseLayer = mPolicy.windowTypeToLayerLw( attachedWindow.mAttrs.type) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type); mAttachedWindow = attachedWindow; mAttachedWindow.mChildWindows.add(this); mLayoutAttached = mAttrs.type != WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; mIsImWindow = attachedWindow.mAttrs.type == TYPE_INPUT_METHOD || attachedWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG; mIsWallpaper = attachedWindow.mAttrs.type == TYPE_WALLPAPER; mIsFloatingLayer = mIsImWindow || mIsWallpaper; } else { // The multiplier here is to reserve space for multiple // windows in the same type layer. mBaseLayer = mPolicy.windowTypeToLayerLw(a.type) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; mSubLayer = 0; mAttachedWindow = null; mLayoutAttached = false; mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD || mAttrs.type == TYPE_INPUT_METHOD_DIALOG; mIsWallpaper = mAttrs.type == TYPE_WALLPAPER; mIsFloatingLayer = mIsImWindow || mIsWallpaper; }
這段代碼初始化WindowState類的如下七個成員變量:
-mBaseLayer:這是一個整型變量,用來描述一個窗口的基礎Z軸位置值,這個值是與窗口類型相關的。對於子窗口來講,它的值由父窗口的基礎Z軸位置值乘以常量TYPE_LAYER_MULTIPLIER再加固定偏移量TYPE_LAYER_OFFSET獲得;對於非子窗口來講,它的值就是由窗口的類型來決定的。一個窗口的基礎Z軸位置值是經過調用WindowManagerService類的成員變量mPolicy所描述的一個窗口管理策略器的成員函數windowTypeToLayerLw來得到的,而窗口管理策略器的成員函數windowTypeToLayerLw主要是根據窗口的類型來決定它的基礎Z軸位置值的。
-mSubLayer:這是一個整型變量,用來描述一個子窗口相對其父窗口的Z軸偏移值。對於非子窗口來講,這個值固定爲0;對於子窗口來講,這個值是由WindowManagerService類的成員變量mPolicy所描述的一個窗口管理策略器的成員函數subWindowTypeToLayerLw來得到的,而窗口管理策略器的成員函數subWindowTypeToLayerLw主要是根據子窗口的類型來決定它相對其父窗口的Z軸偏移值的。
-mAttachedWindow:指向一個WindowState對象,用來描述一個子窗口的父窗口。對於非子窗口來講,這個值固定爲null;對於子窗口來講, 這個值使用參數attachedWindow來初始化。若是當前所建立的WindowState對象所描述的窗口是一個子窗口,那麼這個子窗口還會被添加用來描述它的父窗口的一WindowState對象的成員變量mChildWindows所描述的一個子窗口列表中去。
-mLayoutAttached:這是一個布爾變量,用來描述一個子窗口的視圖是不是嵌入在父窗口的視圖裏面的。對於非子窗口來講,這個值固定爲false;對於子窗口來講,這個值只有子窗口的類型是非對話框時,它的值纔會等於true,不然都等於false。
-mIsImWindow:這是一個布爾變量,表示當前所建立的WindowState對象所描述的窗口是不是一個輸入法窗口或者一個輸入法對話框。
-mIsWallpaper :這是一個布爾變量,表示當前所建立的WindowState對象所描述的窗口是不是一個壁紙窗口。
-mIsFloatingLayer :這是一個布爾變量,表示當前所建立的WindowState對象所描述的窗口是不是一個浮動窗口。當一個窗口是一個輸入法窗口、輸入法對話框口或者壁紙窗口時,它纔是一個浮動窗口。
咱們繼續向前看代碼:
WindowState appWin = this; while (appWin.mAttachedWindow != null) { appWin = mAttachedWindow; } WindowToken appToken = appWin.mToken; while (appToken.appWindowToken == null) { WindowToken parent = mTokenMap.get(appToken.token); if (parent == null || appToken == parent) { break; } appToken = parent; } mRootToken = appToken; mAppToken = appToken.appWindowToken;
這段代碼主要用來初始化成員變量mRootToken和mAppToken。
成員變量mRootToken的類型爲WindowToken,用來描述當前所建立的WindowState對象所描述的窗口的根窗口。若是當前所建立的WindowState對象所描述的窗口是一個子窗口,那麼就先找到它的父窗口,而後再找到它的父窗口所屬的應用程序窗口,即Activity組件窗口,這時候找到的Activity組件窗口就是一個根窗口。若是當前所建立的WindowState對象所描述的窗口是一個子窗口,可是它不屬於任何一個應用程序窗口的,那麼它的父窗口就是一個根窗口。若是當前所建立的WindowState對象所描述的窗口不是一個子窗口,而且它也不屬於一個應用程序窗口的,那麼它自己就是一個根窗口。
成員變量mAppToken的類型爲AppWindowToken,只有當成員變量mRootToken所描述的一個根窗口是一個應用程序窗口時,它的值纔不等於null。
咱們繼續向前看最後一段代碼:
mSurface = null; mRequestedWidth = 0; mRequestedHeight = 0; mLastRequestedWidth = 0; mLastRequestedHeight = 0; mXOffset = 0; mYOffset = 0; mLayer = 0; mAnimLayer = 0; mLastLayer = 0; } ...... } ...... }
這段代碼將如下十個成員變量的值設置爲null或者0:
-mSurface:指向一個mSurface對象,用來描述窗口的繪圖表面。
-mRequestedWidth:這是一個整型變量,用來描述應用程序進程所請求的窗口寬度。
-mRequestedHeight:這是一個整型變量,用來描述應用程序進程所請求的窗口高度。
-mLastRequestedWidth:這是一個整型變量,用來描述應用程序進程上一次所請求的窗口寬度。
-mLastRequestedHeight:這是一個整型變量,用來描述應用程序進程上一次所請求的窗口高度。
-mXOffset:這是一個整型變量,用來描述壁紙窗口相對屏幕在X軸上的偏移量,對其它類型的窗口爲說,這個值等於0。
-mYOffset:這是一個整型變量,用來描述壁紙窗口相對屏幕在Y軸上的偏移量,對其它類型的窗口爲說,這個值等於0。
-mLayer:這是一個整型變量,用來描述窗口的Z軸位置值。
-mAnimLayer:這是一個整型變量,用來描述窗口的Z軸位置值,它的值可能等於mLayer的值,可是在如下四種狀況中不相等。當一個窗口關聯有一個目標窗口的時候,那麼它的值就等於mLayer的值加上目標窗口指定的一個動畫層調整值;當一個窗口的根窗口是一個應用程序窗口時,那麼它的值就等於mLayer的值加上根窗口指定的一個動畫層調整值;當一個窗口是一個輸入法窗口時,那麼它的值就等於mLayer的值加上系統設置的輸入法動畫層調整值;當一個窗口是壁紙窗口時,那麼它的值就等於mLayer的值加上系統設置的壁紙動畫層調整值。
-mLastLayer:這是一個整型變量,用描述窗口上一次所使用的mAnimLayer的值。
至此,咱們就分析完成WindowState的構造函數的實現了,返回到前面的Step 3中,即WindowManagerService類的成員函數addWindow中,接下來就會繼續調用前面所建立的一個WindowState對象的成員函數attach來建立一個關聯的SurfaceSession對象,以即可以用來和SurfaceFlinger服務通訊。
Step 5. WindowState.attach
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private final class WindowState implements WindowManagerPolicy.WindowState { final Session mSession; ...... void attach() { ...... mSession.windowAddedLocked(); } ...... } ...... }
這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
WindowState類的成員變量mSession指向的是一個Session對象,這個Session對象就是用來鏈接應用程序進程和WindowManagerService服務,WindowState類的成員函數attach調用它的成員函數windowAddedLocked來檢查是否須要爲當前正在請求增長窗口的應用程序進程建立一個SurfaceSession對象。
接下來,咱們繼續分析Session類的成員函數windowAddedLocked的實現。
Step 6. Session.windowAddedLocked
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... /** * All currently active sessions with clients. */ final HashSet<Session> mSessions = new HashSet<Session>(); ...... private final class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { ...... SurfaceSession mSurfaceSession; int mNumWindow = 0; ...... void windowAddedLocked() { if (mSurfaceSession == null) { ...... mSurfaceSession = new SurfaceSession(); ...... mSessions.add(this); } mNumWindow++; } ...... } ...... }
這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
Session類的成員變量mSurfaceSession指向的是一個SurfaceSession對象,這個SurfaceSession對象是WindowManagerService服務用來與SurfaceSession服務創建鏈接的。Session類的成員函數windowAddedLocked首先檢查這個成員變量的值是否等於null。若是等於null的話,那麼就說明WindowManagerService服務還沒有爲當前正在請求增長窗口的應用程序進程建立一個用來鏈接SurfaceSession服務的SurfaceSession對象,所以,Session類的成員函數windowAddedLocked就會建立一個SurfaceSession對象,而且保存在成員變量mSurfaceSession中,而且將正在處理的Session對象添加WindowManagerService類的成員變量mSession所描述的一個HashSet中去,表示WindowManagerService服務又多了一個激活的應用程序進程鏈接。
Session類的另一個成員變量mNumWindow是一個整型變量,用來描述當前正在處理的Session對象內部包含有多少個窗口,即運行在引用了當前正在處理的Session對象的應用程序進程的內部的窗口的數量。每當運行在應用程序進程中的窗口銷燬時,該應用程序進程就會通知WindowManagerService服務移除用來描述該窗口狀態的一個WindowState對象,而且通知它所引用的Session對象減小其成員變量mNumWindow的值。當一個Session對象的成員變量mNumWindow的值減小爲0時,就說明該Session對象所描述的應用程序進程鏈接已經不須要了,所以,該Session對象就能夠殺掉其成員變量mSurfaceSession所描述的一個SurfaceSession對象,以即可以斷開和SurfaceSession服務的鏈接。
接下來,咱們就繼續分析SurfaceSession類的構造函數的實現,以即可以瞭解一個SurfaceSession對象是如何與SurfaceSession服務創建鏈接的。
Step 7. new SurfaceSession
public class SurfaceSession { /** Create a new connection with the surface flinger. */ public SurfaceSession() { init(); } ...... private native void init(); ...... private int mClient; }
這個函數定義在文件frameworks/base/core/java/android/view/SurfaceSession.java中。
SurfaceSession類的構造函數的實現很簡單,它只是調用了另一個成員函數init來執行一些初始化操做,其實就是用來初始化SurfaceSession類的成員變量mClient。
SurfaceSession類的成員函數init是一個JNI方法,它是由C++層的函數SurfaceSession_init來實現的,以下所示:
static void SurfaceSession_init(JNIEnv* env, jobject clazz) { sp<SurfaceComposerClient> client = new SurfaceComposerClient; client->incStrong(clazz); env->SetIntField(clazz, sso.client, (int)client.get()); }
這個函數定義在文件frameworks/base/core/jni/android_view_Surface.cpp中。
在分析函數SurfaceSession_init的實現以前,咱們首先看看全局變量sso的定義,以下所示:
struct sso_t { jfieldID client; }; static sso_t sso;
它是一個類型爲sso_t的結構體變量,它的成員變量client描述的是SurfaceSession類的成員變量mClient在SurfaceSession類中的偏移量:
void nativeClassInit(JNIEnv* env, jclass clazz) { ...... jclass surfaceSession = env->FindClass("android/view/SurfaceSession"); sso.client = env->GetFieldID(surfaceSession, "mClient", "I"); ...... }
回到函數SurfaceSession_init中,它首先建立一個SurfaceComposerClient對象client,接着再增長這個SurfaceComposerClient對象的強引用計數,由於再接下來會將這個SurfaceComposerClient對象的地址值保存在參數clazz所描述的一個SurfaceSession對象的成員變量mClient中,這至關因而參數clazz所描述的一個SurfaceSession對象引用了剛纔所建立的SurfaceComposerClient對象client。
在前面Android應用程序與SurfaceFlinger服務的關係概述和學習計劃這一系列的文章中,咱們已經分析過SurfaceComposerClient類的做用了,這主要就是用來在應用程序進程和SurfaceFlinger服務之間創建鏈接的,以便應用程序進程能夠爲運行在它裏面的應用程序窗口請求SurfaceComposerClient建立繪製表面(Surface)的操做等。
這樣,每個Java層的SurfaceSession對象在C++層就都有一個對應的SurfaceComposerClient對象,所以,Java層的應用程序就能夠經過SurfaceSession類來和SurfaceFlinger服務創建鏈接。
至此,咱們就分析完成一個WindowState對象的建立過程了,經過這個過程咱們就能夠知道,每個Activity組件窗口在WindowManagerService服務內部都有一個對應的WindowState對象,用來描述它的窗口狀態。
至此,咱們也分析完成Android應用程序窗口與WindowManagerService服務的鏈接過程了。從這個鏈接過程以及前面Android應用程序窗口(Activity)的窗口對象(Window)的建立過程分析和Android應用程序窗口(Activity)的視圖對象(View)的建立過程分析這兩篇文章,咱們就能夠知道,爲了實現一個Activity組件的UI,不管是應用程序進程,仍是WindowManagerService,都作了大量的工做,例如,應用程序進程爲它建立一個窗口(Window)對象、一個視圖(View)對象、一個ViewRoot對象、一個W對象,WindowManagerService服務爲它建立一個AppWindowToken對象和一個WindowState對象。此外,WindowManagerService服務還爲一個Activity組件所運行在的應用程序進程建立了一個Session對象。理解這些對象的實現以及做用對咱們瞭解Android應用程序窗口的實現框架以及WindowManagerService服務的實現原理都是很是重要的。
雖然到目前爲止,咱們已經爲Android應用程序窗口建立了不少對象,可是咱們仍然還有一個最重要的對象尚未建立,那就是Android應用程序窗口的繪圖表面,即用來渲染UI的Surface尚未建立。從前面Android應用程序與SurfaceFlinger服務的關係概述和學習計劃這一系列的文章能夠知道,這個Surface是要請求SurfaceFlinger服務來建立的,所以,在接下來的一篇文章中,咱們就將繼續分析Android應用程序窗口的繪圖表面(Surface)的建立過程,敬請關注!