在前兩文中,我們分析了Activity元件的視窗對象和視圖對象的建立過程。Activity元件在其視窗對象和視圖對象建立完成之後,就會請求與WindowManagerService建立一個連接配接,即請求WindowManagerService為其增加一個WindowState對象,用來描述它的視窗狀态。在本文中,我們就詳細分析Activity元件與WindowManagerService的連接配接過程。
《Android系統源代碼情景分析》一書正在進擊的程式員網(http://0xcc0xcd.com)中連載,點選進入!
我們從兩方面來看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元件的視窗狀态。
上述Activity元件與WindowManagerService服務之間的連接配接模型如圖1所示:

圖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所示:
圖2 W類的實作
圖3 Session類和WindowState類的實作
W類實作了IWindow接口,它的類執行個體是一個Binder本地對象。從前面Android應用程式視窗(Activity)的視圖對象(View)的建立過程分析一文可以知道,一個Activity元件在啟動的過程中,會建立一個關聯的ViewRoot對象,用來配合WindowManagerService服務來管理該Activity元件的視窗狀态。在這個ViewRoot對象内部,有一個類型為W的成員變量mWindow,它是在ViewRoot對象的建立過程中建立的。
ViewRoot類有一個靜态成員變量sWindowSession,它指向了一個實作了IWindowSession接口的Session代理對象。當應用程式程序啟動第一個Activity元件的時候,它就會請求WindowManagerService服務發送一個建立連接配接的Binder程序間通信請求。WindowManagerService服務接收到這個請求之後,就會在内部建立一個類型為Session的Binder本地對象,并且将這個Binder本地對象傳回給應用程式程序,後者于是就會得到一個Session代理對象,并且儲存在ViewRoot類的靜态成員變量sWindowSession中。
有了這個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)的建立過程,敬請關注!
老羅的新浪微網誌:http://weibo.com/shengyangluo,歡迎關注!