WindowManagerService服務(wù)是如何以堆棧的形式來(lái)組織窗口
我們知道,在Android系統(tǒng)中,Activity是以堆棧的形式組織在ActivityManagerService服務(wù)中的。與Activity類(lèi)似,Android系統(tǒng)中的窗口也是以堆棧的形式組織在WindowManagerService服務(wù)中的,其中,Z軸位置較低的窗口位于Z軸位置較高的窗口的下面。在本文中,我們就詳細(xì)分析WindowManagerService服務(wù)是如何以堆棧的形式來(lái)組織窗口的。
從前面Android應(yīng)用程序啟動(dòng)過(guò)程源代碼分析一文可以知道,應(yīng)用程序進(jìn)程中的每一個(gè)Activity組件在Activity管理服務(wù)ActivityManagerService中都對(duì)應(yīng)有一個(gè)ActivityRecord對(duì)象。從前面Android應(yīng)用程序窗口(Activity)與WindowManagerService服務(wù)的連接過(guò)程分析一文又可以知道,Activity管理服務(wù)ActivityManagerService中每一個(gè)ActivityRecord對(duì)象在Window管理服務(wù)WindowManagerService中都對(duì)應(yīng)有一個(gè)AppWindowToken對(duì)象。
此外,在輸入法管理服務(wù)InputMethodManagerService中,每一個(gè)輸入法窗口都對(duì)應(yīng)有一個(gè)Binder對(duì)象,這個(gè)Binder對(duì)象在Window管理服務(wù)WindowManagerService又對(duì)應(yīng)有一個(gè)WindowToken對(duì)象。
與輸入法窗口類(lèi)似,在壁紙管理服務(wù)WallpaperManagerService中,每一個(gè)壁紙窗口都對(duì)應(yīng)有一個(gè)Binder對(duì)象,這個(gè)Binder對(duì)象在Window管理服務(wù)WindowManagerService也對(duì)應(yīng)有一個(gè)WindowToken對(duì)象。
在Window管理服務(wù)WindowManagerService中,無(wú)論是AppWindowToken對(duì)象,還是WindowToken對(duì)象,它們都是用來(lái)描述一組有著相同令牌的窗口的,每一個(gè)窗口都是通過(guò)一個(gè)WindowState對(duì)象來(lái)描述的。例如,一個(gè)Activity組件窗口可能有一個(gè)啟動(dòng)窗口(Starting Window),還有若干個(gè)子窗口,那么這些窗口就會(huì)組成一組,并且都是以Activity組件在Window管理服務(wù)WindowManagerService中所對(duì)應(yīng)的AppWindowToken對(duì)象為令牌的。從抽象的角度來(lái)看,就是在Window管理服務(wù)WindowManagerService中,每一個(gè)令牌(AppWindowToken或者WindowToken)都是用來(lái)描述一組窗口(WindowState)的,并且每一個(gè)窗口的子窗口也是與它同屬于一個(gè)組,即都有著相同的令牌。
上述的窗口組織方式如圖1所示:
圖1 窗口在WindowManagerService服務(wù)中的組織方式
其中,Activity Stack是在ActivityManagerService服務(wù)中創(chuàng)建的,Token List和Window Stack是在WindowManagerService中創(chuàng)建的,而B(niǎo)inder for IM和Binder for WP分別是在InputMethodManagerService服務(wù)和WallpaperManagerService服務(wù)中創(chuàng)建的,用來(lái)描述一個(gè)輸入法窗口和一個(gè)壁紙窗口。
圖1中的對(duì)象的對(duì)應(yīng)關(guān)系如下所示:
1. ActivityRecord-J對(duì)應(yīng)于AppWindowToken-J,后者描述的一組窗口是{WindowState-A, WindowState-B, WindowState-B-1},其中, WindowState-B-1是WindowState-B的子窗口。
2. ActivityRecord-K對(duì)應(yīng)于AppWindowToken-K,后者描述的一組窗口是{WindowState-C, WindowState-C-1, WindowState-D, WindowState-D-1},其中, WindowState-C-1是WindowState-C的子窗口,WindowState-D-1是WindowState-D的子窗口。
3. ActivityRecord-N對(duì)應(yīng)于AppWindowToken-N,后者描述的一組窗口是{WindowState-E},其中, WindowState-E是系統(tǒng)當(dāng)前激活的Activity窗口。
4. Binder for IM對(duì)應(yīng)于WindowToken-I,后者描述的一組窗口是{WindowState-I},其中, WindowState-I是WindowState-E的輸入法窗口。
5. Binder for WP對(duì)應(yīng)于WindowToken-W,后者描述的一組窗口是{WindowState-W},其中, WindowState-W是WindowState-E的壁紙窗口。
從圖1還可以知道,Window Stack中的WindowState是按照它們所描述的窗口的Z軸位置從低到高排列的。
以上就是WindowManagerService服務(wù)組織系統(tǒng)中的窗口的抽象模型,接下來(lái)我們將分析AppWindowToken、WindowToken和WindowState的一些增加、移動(dòng)和刪除等操作,以便可以對(duì)這個(gè)抽象模型有一個(gè)更深刻的認(rèn)識(shí)。
1. 增加AppWindowToken
從前面Android應(yīng)用程序窗口(Activity)與WindowManagerService服務(wù)的連接過(guò)程分析一文可以知道,一個(gè)Activity組件在啟動(dòng)的過(guò)程中,ActivityManagerService服務(wù)會(huì)調(diào)用調(diào)用WindowManagerService類(lèi)的成員函數(shù)addAppToken來(lái)為它增加一個(gè)AppWindowToken,如下所示:
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) {
......
synchronized(mWindowMap) {
AppWindowToken wtoken = findAppWindowToken(token.asBinder());
if (wtoken != null) {
......
return;
}
wtoken = new AppWindowToken(token);
......
mAppTokens.add(addPos, wtoken);
......
mTokenMap.put(token.asBinder(), wtoken);
mTokenList.add(wtoken);
......
}
}
......
}
這個(gè)函數(shù)定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
WindowManagerService類(lèi)有三個(gè)成員變量mTokenMap、mTokenList和mAppTokens,它們都是用來(lái)描述系統(tǒng)中的窗口的。
成員變量mTokenMap指向的是一個(gè)HashMap,它里面保存的是一系列的WindowToken對(duì)象,每一個(gè)WindowToken對(duì)象都是用來(lái)描述一個(gè)窗口的,并且是以描述這些窗口的一個(gè)Binder對(duì)象的IBinder接口為鍵值的。例如,對(duì)于Activity組件類(lèi)型的窗口來(lái)說(shuō),它們分別是以用來(lái)描述它們的一個(gè)ActivityRecord對(duì)象的IBinder接口保存在成員變量mTokenMap所指向的一個(gè)HashMap中的。
成員變量mTokenList指向的是一個(gè)ArrayList,它里面保存的也是一系列WindowToken對(duì)象,這些WindowToken對(duì)象與保存在成員變量mTokenMap所指向的一個(gè)HashMap中的WindowToken對(duì)象是一樣的。成員變量mTokenMap和成員變量mTokenList的區(qū)別就在于,前者在給定一個(gè)IBinder接口的情況下,可以迅速指出是否存在一個(gè)對(duì)應(yīng)的WindowToken對(duì)象,而后者可以迅速遍歷WindowManagerService服務(wù)中的WindowToken對(duì)象。
成員變量mAppTokens指向的也是一個(gè)ArrayList,不過(guò)它里面保存的是一系列AppWindowToken對(duì)象,每一個(gè)AppWindowToken對(duì)象都是用來(lái)描述一個(gè)Activity組件窗口的,而這些AppWindowToken對(duì)象是以它們描述的窗口的Z軸坐標(biāo)由小到大保存在這個(gè)ArrayList中的,這樣我們就可以通過(guò)這個(gè)ArrayList來(lái)從上到下或者從下到上地遍歷系統(tǒng)中的所有Activity組件窗口。由于這些AppWindowToken對(duì)象所描述的Activity組件窗口也是一個(gè)窗口,并且AppWindowToken類(lèi)是從WindowToken繼承下來(lái)的,因此,這些AppWindowToken對(duì)象還會(huì)同時(shí)被保存在成員變量mTokenMap所指向的一個(gè)HashMap和成員變量mTokenList所指向的一個(gè)ArrayList中。
理解了WindowManagerService類(lèi)的這三個(gè)成員變量的含義之后,它的成員函數(shù)addAppToken的實(shí)現(xiàn)就好理解了,其中,參數(shù)token指向的便是用來(lái)描述正在啟動(dòng)的Activity組件所對(duì)應(yīng)的一個(gè)ActivityRecord對(duì)象,而參數(shù)addPos用來(lái)描述該Activity組件在堆棧中的位置,這個(gè)位置同時(shí)也是接下來(lái)要?jiǎng)?chuàng)建的AppWindowToken對(duì)象在WindowManagerService類(lèi)的mTokenList所描述的一個(gè)ArrayList中的位置。
WindowManagerService類(lèi)的成員函數(shù)addAppToken首先調(diào)用另外一個(gè)成員函數(shù)findAppWindowToken來(lái)在成員變量mTokenMap所描述的一個(gè)HashMap檢查是否已經(jīng)存在一個(gè)AppWindowToken。如果已經(jīng)存在的話(huà),那么WindowManagerService類(lèi)的成員函數(shù)addAppToken就什么也不做就返回了,否則的話(huà),就會(huì)使用參數(shù)token來(lái)創(chuàng)建一個(gè)AppWindowToken對(duì)象,并且會(huì)將該AppWindowToken對(duì)象分別保存在WindowManagerService類(lèi)的成員變量mTokenMap、mTokenList和mAppTokens中。
2. 刪除AppWindowToken
刪除AppWindowToken是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)removeAppTokensLocked來(lái)實(shí)現(xiàn)的,如下所示:
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
private void removeAppTokensLocked(List<IBinder> tokens) {
// XXX This should be done more efficiently!
// (take advantage of the fact that both lists should be
// ordered in the same way.)
int N = tokens.size();
for (int i=0; i<N; i++) {
IBinder token = tokens.get(i);
final AppWindowToken wtoken = findAppWindowToken(token);
if (!mAppTokens.remove(wtoken)) {
......
i--;
N--;
}
}
}
......
}
這個(gè)函數(shù)定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
WindowManagerService類(lèi)的成員函數(shù)removeAppTokensLocked可以同時(shí)刪除一組AppWindowToken對(duì)象。
參數(shù)tokens所描述的是一個(gè)IBinder接口列表,與這些IBinder接口所對(duì)應(yīng)的AppWindowToken對(duì)象就是接下來(lái)要?jiǎng)h除的。WindowManagerService類(lèi)的成員函數(shù)removeAppTokensLocked通過(guò)一個(gè)for循環(huán)來(lái)依次調(diào)用另外一個(gè)成員函數(shù)findAppWindowToken,以便可以找到保存在列表tokens中的每一個(gè)IBinder接口所對(duì)應(yīng)的AppWindowToken對(duì)象,然后將該AppWindowToken對(duì)象從WindowManagerService類(lèi)的成員變量mAppTokens所描述的一個(gè)ArrayList中刪除。
注意,WindowManagerService類(lèi)的成員函數(shù)removeAppTokensLocked是在內(nèi)部使用的,它只是把一個(gè)AppWindowToken對(duì)象從成員變量mAppTokens中刪除,而沒(méi)有從另外兩個(gè)成員變量mTokenMap和mTokenList中刪除。
3. 移動(dòng)AppWindowToken至指定位置
移動(dòng)AppWindowToken至指定位置是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)moveAppToken來(lái)實(shí)現(xiàn)的,如下所示:
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
public void moveAppToken(int index, IBinder token) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"moveAppToken()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
synchronized(mWindowMap) {
......
final AppWindowToken wtoken = findAppWindowToken(token);
if (wtoken == null || !mAppTokens.remove(wtoken)) {
......
return;
}
mAppTokens.add(index, wtoken);
......
final long origId = Binder.clearCallingIdentity();
......
if (tmpRemoveAppWindowsLocked(wtoken)) {
......
reAddAppWindowsLocked(findWindowOffsetLocked(index), wtoken);
......
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
Binder.restoreCallingIdentity(origId);
}
}
......
}
這個(gè)函數(shù)定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
參數(shù)token描述的是要移動(dòng)的AppWindowToken對(duì)象所對(duì)應(yīng)的一個(gè)IBinder接口,而參數(shù)index描述的是該AppWindowToken對(duì)象要移動(dòng)到的位置。注意,移動(dòng)一個(gè)AppWindowToken對(duì)象到指定的位置是需要android.Manifest.permission.MANAGE_APP_TOKENS權(quán)限的。
WindowManagerService類(lèi)的成員函數(shù)moveAppToken首先找到與參數(shù)token所對(duì)應(yīng)的AppWindowToken對(duì)象,并且將該AppWindowToken對(duì)象從WindowManagerService類(lèi)的成員變量mAppTokens所描述的一個(gè)ArrayList中移除,這樣做的目的是為了接下來(lái)可以將該AppWindowToken對(duì)象移動(dòng)至該ArrayList中的指定位置上,即參數(shù)index所描述的位置上。
注意,上述操作只是將參數(shù)token所對(duì)應(yīng)的AppWindowToken對(duì)象移動(dòng)到了WindowManagerService類(lèi)的成員變量mAppTokens所描述的一個(gè)ArrayList的指定位置上,接下來(lái)還需要同時(shí)將與該AppWindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象移動(dòng)至WindowManagerService服務(wù)內(nèi)部的一個(gè)WindowState堆棧合適位置上去。
移動(dòng)對(duì)應(yīng)的WindowState對(duì)象的操作同樣也是分兩步執(zhí)行的:第一步先調(diào)用WindowManagerService類(lèi)的成員函數(shù)tmpRemoveAppWindowsLocked來(lái)將這些WindowState對(duì)象從原來(lái)的WindowState堆棧位置移除;第二步再調(diào)用WindowManagerService類(lèi)的成員函數(shù)reAddAppWindowsLocked來(lái)將這些WindowState對(duì)象插入到WindowState堆棧的合適位置去。
對(duì)應(yīng)的WindowState對(duì)象被移動(dòng)到的合適位置是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)findWindowOffsetLocked來(lái)獲得的,它的實(shí)現(xiàn)如下所示:
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
/**
* Z-ordered (bottom-most first) list of all Window objects.
*/
final ArrayList<WindowState> mWindows = new ArrayList<WindowState>();
......
private int findWindowOffsetLocked(int tokenPos) {
final int NW = mWindows.size();
if (tokenPos >= mAppTokens.size()) {
int i = NW;
while (i > 0) {
i--;
WindowState win = mWindows.get(i);
if (win.getAppToken() != null) {
return i+1;
}
}
}
while (tokenPos > 0) {
// Find the first app token below the new position that has
// a window displayed.
final AppWindowToken wtoken = mAppTokens.get(tokenPos-1);
......
if (wtoken.sendingToBottom) {
......
tokenPos--;
continue;
}
int i = wtoken.windows.size();
while (i > 0) {
i--;
WindowState win = wtoken.windows.get(i);
int j = win.mChildWindows.size();
while (j > 0) {
j--;
WindowState cwin = win.mChildWindows.get(j);
if (cwin.mSubLayer >= 0) {
for (int pos=NW-1; pos>=0; pos--) {
if (mWindows.get(pos) == cwin) {
......
return pos+1;
}
}
}
}
for (int pos=NW-1; pos>=0; pos--) {
if (mWindows.get(pos) == win) {
......
return pos+1;
}
}
}
tokenPos--;
}
return 0;
}
......
}
這個(gè)函數(shù)定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
參數(shù)tokenPos描述的是一個(gè)AppWindowToken對(duì)象在WindowManagerService類(lèi)的成員變量mAppTokens所描述的一個(gè)ArrayList的位置,WindowManagerService類(lèi)的成員函數(shù)findWindowOffsetLocked的目標(biāo)就要找到與該AppWindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象在WindowManagerService服務(wù)內(nèi)部的一個(gè)WindowState堆棧的起始偏移位置。有了這個(gè)起始偏移位置之后,我們就可以將對(duì)應(yīng)的所有WindowState對(duì)象有序地插入到該WindowState堆棧中去。WindowManagerService服務(wù)內(nèi)部的WindowState堆棧是通過(guò)WindowManagerService類(lèi)的成員變量mWindows來(lái)描述的。接下來(lái)我們就分兩種情況來(lái)分析這個(gè)起始偏移位置的計(jì)算過(guò)程。
第一種情況是參數(shù)tokenPos的值大于WindowManagerService類(lèi)的成員變量mAppTokens所描述的一個(gè)ArrayList的大小。這是一種異常情況,一般來(lái)說(shuō),參數(shù)tokenPos是指向mAppTokens列表的某一個(gè)位置的,不過(guò)這時(shí)候意味著它所描述的AppWindowToken對(duì)象的Z軸位置要大于mAppTokens列表的最上面的一個(gè)AppWindowToken對(duì)象的Z軸位置的。這也就是說(shuō),與參數(shù)tokenPos所描述的AppWindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象的要位于與mAppTokens列表的最上面的一個(gè)AppWindowToken對(duì)象所對(duì)應(yīng)的任一個(gè)WindoState對(duì)象的上面。因此,就需要找到與mAppTokens列表的最上面的一個(gè)AppWindowToken對(duì)象所對(duì)應(yīng)的Z軸位置最大的一個(gè)WindoState對(duì)象在WindowState堆棧中的位置i,然后就可以知道與參數(shù)tokenPos所描述的AppWindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象在WindowState堆棧的起始偏移位置為i+1。
如何找到mAppTokens列表的最上面的一個(gè)AppWindowToken對(duì)象所對(duì)應(yīng)的Z軸位置最大的一個(gè)WindoState對(duì)象在WindowState堆棧中的位置i呢?從圖1可以可得到一個(gè)結(jié)論:WindowManagerService服務(wù)內(nèi)部中的所有WindowState對(duì)象都是按照Z(yǔ)軸從位置從小到大排列在WindowState堆棧中的,并且在mAppTokens列表中,位于上面的一個(gè)AppWindowToken對(duì)象所對(duì)應(yīng)的那些WindowState對(duì)象的Z軸位置是一定大于位于下面的一個(gè)AppWindowToken對(duì)象所對(duì)應(yīng)的那些WindowState對(duì)象的Z軸位置的。因此,我們只要從WindowState堆棧的頂端開(kāi)始往下遍歷,找到這樣的一個(gè)WindowState對(duì)象,它是屬于一個(gè)AppWindowToken對(duì)象的,即它的成員函數(shù)getAppToken的返回值不等于null,那么它在WindowState堆棧中的位置就是我們要找到的位置i。有了這個(gè)位置i之后,將它的值加上1,就可以得到參數(shù)t所描述的AppWindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象在WindowState堆棧的起始偏移位置了。
第二種情況是參數(shù)tokenPos的值小于WindowManagerService類(lèi)的成員變量mAppTokens所描述的一個(gè)ArrayList的大小。根據(jù)前面得到的推論,我們只要在mAppTokens列表中找到一個(gè)AppWindowToken對(duì)象,它滿(mǎn)足以下三個(gè)條件:
A. 它在mAppTokens列表中的位置小于tokenPos;
B. 它在WindowState堆棧中對(duì)應(yīng)有WindowState對(duì)象;
C. 它不是將要置于WindowState堆棧的底部。
如果一個(gè)AppWindowToken對(duì)象在WindowState堆棧中對(duì)應(yīng)有WindowState對(duì)象,那么這些WindowState對(duì)象也會(huì)同時(shí)按照Z(yǔ)軸從小到大的順序保存它的成員變量windows所描述的一個(gè)ArrayList中,這意味著如果一個(gè)AppWindowToken對(duì)象滿(mǎn)足條件B,那么它的成員變量windows所描述的一個(gè)ArrayList的大小就大于0。
如果一個(gè)AppWindowToken對(duì)象不是將要置于WindowState堆棧的底部,那么它的成員變量sendingToBottom的值就不等于true,這也意味這個(gè)AppWindowToken對(duì)象滿(mǎn)足條件C。
如果能找到滿(mǎn)足上述條件的一個(gè)AppWindowToken對(duì)象wtoken,那么我們只要找到與它所對(duì)應(yīng)的Z軸位置最大的WindowState對(duì)象在WindowManagerService服務(wù)內(nèi)部的WindowState堆棧中的位置i,那么將它的值加1,就可以得到與參數(shù)tokenPos所描述的AppWindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象在WindowState堆棧的起始偏移位置了。
那么如何找到與這個(gè)AppWindowToken對(duì)象wtoken對(duì)應(yīng)的Z軸位置最大的WindowState對(duì)象在WindowManagerService服務(wù)內(nèi)部的WindowState堆棧中的位置i呢?從前面的圖1可以知道,一個(gè)AppWindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象可以劃分為兩種類(lèi)型:第一種類(lèi)型是父窗口類(lèi)型的;第二種是子窗口類(lèi)型的。如果一個(gè)WindowState對(duì)象所描述的窗口是父窗口,那么它的子窗口就保存在它的成員變量mChildWindows所描述的一個(gè)ArrayList中,并且這些子窗口是按照Z(yǔ)軸位置從小到大的順序排列的,同時(shí),該WindowState對(duì)象也會(huì)保存在與它所對(duì)應(yīng)的一個(gè)AppWindowToken對(duì)象的成員變量windows所描述的一個(gè)ArrayList中。
有了上述結(jié)論,并且假設(shè)存在一個(gè)能夠滿(mǎn)足上述三個(gè)條件的AppWindowToken對(duì)象wtoken,那么就可以從上到下遍歷保存在它的成員變量windows所描述的一個(gè)ArrayList中的每一個(gè)WindowState對(duì)象win:
I. 如果WindowState對(duì)象win所描述的一個(gè)窗口具有子窗口,那么就繼續(xù)從上到下遍歷這些子窗口,即從上到下遍歷WindowState對(duì)象win的成員變量mChildWindows所描述的一個(gè)ArrayList。如果能找到一個(gè)WindowState對(duì)象cwin,它的成員變量mSubLayer的值大于等于0,那么該WindowState對(duì)象cwin在WindowManagerService服務(wù)內(nèi)部的WindowState堆棧中的位置就是我們要得到的位置i。注意,如果WindowState對(duì)象cwin的成員變量mSubLayer的值小于0,那么它雖然是一個(gè)子窗口,但是它卻是位于父窗口的后面的,即它的Z軸位置是小于父窗口的Z軸位置的。
II. 如果WindowState對(duì)象win所描述的一個(gè)窗口不具有子窗口,即它的成員變量mChildWindows所描述的一個(gè)ArrayList的大小等于0,那么它在WindowManagerService服務(wù)內(nèi)部的WindowState堆棧中的位置就是我們要得到的位置i。
得到了位置i之后,將它的值加1,那么就可以得到與參數(shù)tokenPos所描述的AppWindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象在WindowState堆棧的起始偏移位置了。
回到WindowManagerService類(lèi)的成員函數(shù)moveAppToken中,調(diào)整好參數(shù)token所描述的AppWindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象在WindowState堆棧中的位置之后,即調(diào)用了成員函數(shù)reAddAppWindowsLocked之后,這時(shí)候系統(tǒng)中的窗口的布局就會(huì)發(fā)生了變化,即系統(tǒng)中的窗口的Z軸位置關(guān)系發(fā)生了變化,那么接下來(lái)就需要調(diào)用成員函數(shù)updateFocusedWindowLocked來(lái)重新計(jì)算系統(tǒng)中的窗口的Z軸位置,并且調(diào)用成員函數(shù)performLayoutAndPlaceSurfacesLocked來(lái)重新布局系統(tǒng)中的窗口。
4. 移動(dòng)AppWindowToken至頂端
移動(dòng)AppWindowToken至頂端是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)moveAppTokensToTop來(lái)實(shí)現(xiàn)的,如下所示:
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
public void moveAppTokensToTop(List<IBinder> tokens) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"moveAppTokensToTop()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
final long origId = Binder.clearCallingIdentity();
synchronized(mWindowMap) {
removeAppTokensLocked(tokens);
final int N = tokens.size();
for (int i=0; i<N; i++) {
AppWindowToken wt = findAppWindowToken(tokens.get(i));
if (wt != null) {
mAppTokens.add(wt);
if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
mToTopApps.remove(wt);
mToBottomApps.remove(wt);
mToTopApps.add(wt);
wt.sendingToBottom = false;
wt.sendingToTop = true;
}
}
}
if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET) {
moveAppWindowsLocked(tokens, mAppTokens.size());
}
}
Binder.restoreCallingIdentity(origId);
}
......
}
這個(gè)函數(shù)定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
WindowManagerService類(lèi)的成員函數(shù)moveAppTokensToTop可以同時(shí)將一組AppWindowToken移至頂端,同時(shí)需要調(diào)用者具有android.Manifest.permission.MANAGE_APP_TOKENS權(quán)限。
參數(shù)tokens所描述的是一個(gè)IBinder接口列表,與這些IBinder接口所對(duì)應(yīng)的AppWindowToken對(duì)象就是接下來(lái)要移至頂端的。在將保存在參數(shù)tokens中的IBinder接口所對(duì)應(yīng)的AppWindowToken對(duì)象移至頂端之前,WindowManagerService類(lèi)的成員函數(shù)首先會(huì)調(diào)用前面所描述的成員函數(shù)removeAppTokensLocked來(lái)刪除這些AppWindowToken對(duì)象,然后再依次將它們添加到WindowManagerService類(lèi)的成員變量mAppTokens所描述的一個(gè)ArrayList的末尾去。
注意,WindowManagerService類(lèi)的成員變量mNextAppTransition用來(lái)描述系統(tǒng)當(dāng)前是否正在切換Activity窗口。如果是的話(huà),那么它的值就不等于WindowManagerPolicy.TRANSIT_UNSET,這時(shí)候就需要:
A. 將所有要移至頂端的AppWindowToken對(duì)象都保存在WindowManagerService類(lèi)的另外一個(gè)成員變量mToTopApps所描述的一個(gè)ArrayList中去,并且將這些AppWindowToken對(duì)象的成員變量sendingToTop的值設(shè)置為true。
B. 將所有要移至頂端的AppWindowToken對(duì)象所對(duì)應(yīng)WindowState對(duì)象都移至WindowManagerService服務(wù)內(nèi)部的一個(gè)WindowState堆棧的頂端去,這是通過(guò)調(diào)用另外一個(gè)成員函數(shù)moveAppWindowsLocked來(lái)實(shí)現(xiàn)的。
執(zhí)行完成上述兩個(gè)操作之后,與要移至頂端的AppWindowToken對(duì)象所對(duì)應(yīng)的窗口就會(huì)位于窗口堆棧的最上面了。
5. 移動(dòng)AppWindowToken至底端
移動(dòng)AppWindowToken至頂端是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)moveAppTokensToBottom來(lái)實(shí)現(xiàn)的,如下所示:
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
public void moveAppTokensToBottom(List<IBinder> tokens) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"moveAppTokensToBottom()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
final long origId = Binder.clearCallingIdentity();
synchronized(mWindowMap) {
removeAppTokensLocked(tokens);
final int N = tokens.size();
int pos = 0;
for (int i=0; i<N; i++) {
AppWindowToken wt = findAppWindowToken(tokens.get(i));
if (wt != null) {
mAppTokens.add(pos, wt);
if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
mToTopApps.remove(wt);
mToBottomApps.remove(wt);
mToBottomApps.add(i, wt);
wt.sendingToTop = false;
wt.sendingToBottom = true;
}
pos++;
}
}
if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET) {
moveAppWindowsLocked(tokens, 0);
}
}
Binder.restoreCallingIdentity(origId);
}
......
}
這個(gè)函數(shù)定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
WindowManagerService類(lèi)的成員函數(shù)moveAppTokensToBottom可以同時(shí)將一組AppWindowToken移至底端。將一組AppWindowToken移至底端與將一組AppWindowToken移至頂端的實(shí)現(xiàn)是類(lèi)似的,只不過(guò)是移動(dòng)的方向相反而已。因此,WindowManagerService類(lèi)的成員函數(shù)moveAppTokensToBottom的實(shí)現(xiàn)可以參考前面所分析的成員函數(shù)moveAppTokensToTop的實(shí)現(xiàn),這里不再詳述。
6. 增加WindowToken
從圖1可以知道,如果一個(gè)WindowState對(duì)象不是與一個(gè)AppWindowToken對(duì)象對(duì)應(yīng)的,那么它就必須要與一個(gè)WindowToken對(duì)象對(duì)應(yīng)。例如,用來(lái)描述輸入法窗口和壁紙窗口的WindowState對(duì)象對(duì)應(yīng)的就是WindowToken對(duì)象,而不是AppWindowToken對(duì)象,因?yàn)樗鼈儾皇茿ctivity類(lèi)型的窗口。
輸入法窗口和壁紙窗口分別是由輸入法管理服務(wù)InputMethodManagerService和壁紙管理服務(wù)WallpaperManagerService調(diào)用WindowManagerService類(lèi)的成員函數(shù)addWindowToken來(lái)增加對(duì)應(yīng)的WindowToken對(duì)象的,如下所示:
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
public void addWindowToken(IBinder token, int type) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"addWindowToken()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
synchronized(mWindowMap) {
WindowToken wtoken = mTokenMap.get(token);
if (wtoken != null) {
Slog.w(TAG, "Attempted to add existing input method token: " + token);
return;
}
wtoken = new WindowToken(token, type, true);
mTokenMap.put(token, wtoken);
mTokenList.add(wtoken);
if (type == TYPE_WALLPAPER) {
mWallpaperTokens.add(wtoken);
}
}
}
......
}
這個(gè)函數(shù)定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
調(diào)用WindowManagerService類(lèi)的成員函數(shù)addWindowToken需要具有android.Manifest.permission.MANAGE_APP_TOKENS權(quán)限。
對(duì)于輸入法窗口和壁紙窗口來(lái)說(shuō),參數(shù)token指向的是與它們所關(guān)聯(lián)的一個(gè)Binder對(duì)象的IBinder接口,而參數(shù)type描述的是要在WindowManagerService服務(wù)內(nèi)部增加WindowToken對(duì)象的窗口的類(lèi)型。
WindowManagerService類(lèi)的成員函數(shù)addWindowToken首先檢查在成員變量mTokenMap所描述的一個(gè)HashMap檢查是否已經(jīng)存在一個(gè)WindowToken對(duì)象與參數(shù)token對(duì)應(yīng)。如果已經(jīng)存在的話(huà),那么WindowManagerService類(lèi)的成員函數(shù)addWindowToken就什么也不做就返回了,否則的話(huà),就會(huì)使用參數(shù)token來(lái)創(chuàng)建一個(gè)WindowToken對(duì)象,并且會(huì)將該WindowToken對(duì)象分別保存在WindowManagerService類(lèi)的成員變量mTokenMap和mTokenList中。
這里有兩個(gè)地方需要注意:
A. 由于這里增加的是WindowToken對(duì)象,而不是AppWindowToken對(duì)象,因此,與增加AppWindowToken不同,這里不需要將新創(chuàng)建的WindowToken對(duì)象保存在WindowManagerService類(lèi)的成員變量mAppTokens中。
B. 如果參數(shù)type的值等于TYPE_WALLPAPER,那么就意味著新創(chuàng)建的WindowToken對(duì)象是用來(lái)描述壁紙窗口的,這時(shí)候還需要將新創(chuàng)建的WindowToken對(duì)象保存在WindowManagerService類(lèi)的成員變量mWallpaperTokens所描述的一個(gè)ArrayList中,以方便管理壁紙窗口。
對(duì)于非輸入法窗口、非壁紙窗口以及非Activity窗口來(lái)說(shuō),它們所對(duì)應(yīng)的WindowToken對(duì)象是在它們?cè)黾拥絎indowManagerService服務(wù)的時(shí)候創(chuàng)建的。從前面Android應(yīng)用程序窗口(Activity)與WindowManagerService服務(wù)的連接過(guò)程分析一文可以知道,增加一個(gè)窗口WindowManagerService服務(wù)最終是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)addWindow來(lái)實(shí)現(xiàn)的,接下來(lái)我們就主要分析與創(chuàng)建WindowToken相關(guān)的邏輯,如下所示:
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) {
......
synchronized(mWindowMap) {
......
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;
}
......
if (addToken) {
mTokenMap.put(attrs.token, token);
mTokenList.add(token);
}
......
}
......
}
......
}
這個(gè)函數(shù)定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
如果參數(shù)attrs所描述的一個(gè)WindowManager.LayoutParams對(duì)象的成員變量token所指向的一個(gè)IBinder接口在WindowManagerService類(lèi)的成員變量mTokenMap所描述的一個(gè)HashMap中沒(méi)有一個(gè)對(duì)應(yīng)的WindowToken對(duì)象,并且該WindowManager.LayoutParams對(duì)象的成員變量type的值不等于TYPE_INPUT_METHOD、TYPE_WALLPAPER,以及不在FIRST_APPLICATION_WINDOW和LAST_APPLICATION_WINDOW,那么就意味著這時(shí)候要增加的窗口就既不是輸入法窗口,也不是壁紙窗口和Activity窗口,因此,就需要以參數(shù)attrs所描述的一個(gè)WindowManager.LayoutParams對(duì)象的成員變量token所指向的一個(gè)IBinder接口為參數(shù)來(lái)創(chuàng)建一個(gè)WindowToken對(duì)象,并且將該WindowToken對(duì)象保存在WindowManagerService類(lèi)的成員變量mTokenMap和mTokenList中。
7. 刪除WindowToken
刪除WindowToken是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)removeWindowToken來(lái)實(shí)現(xiàn)的,如下所示:
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
public void removeWindowToken(IBinder token) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"removeWindowToken()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
final long origId = Binder.clearCallingIdentity();
synchronized(mWindowMap) {
WindowToken wtoken = mTokenMap.remove(token);
mTokenList.remove(wtoken);
if (wtoken != null) {
boolean delayed = false;
if (!wtoken.hidden) {
wtoken.hidden = true;
final int N = wtoken.windows.size();
boolean changed = false;
for (int i=0; i<N; i++) {
WindowState win = wtoken.windows.get(i);
if (win.isAnimating()) {
delayed = true;
}
if (win.isVisibleNow()) {
applyAnimationLocked(win,
WindowManagerPolicy.TRANSIT_EXIT, false);
changed = true;
}
}
if (changed) {
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
}
if (delayed) {
mExitingTokens.add(wtoken);
} else if (wtoken.windowType == TYPE_WALLPAPER) {
mWallpaperTokens.remove(wtoken);
}
}
......
} else {
Slog.w(TAG, "Attempted to remove non-existing token: " + token);
}
}
Binder.restoreCallingIdentity(origId);
}
......
}
這個(gè)函數(shù)定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
調(diào)用WindowManagerService類(lèi)的成員函數(shù)removeWindowToken需要具有android.Manifest.permission.MANAGE_APP_TOKENS權(quán)限。
WindowManagerService類(lèi)的成員函數(shù)removeWindowToken首先找到與參數(shù)token所描述的Binder接口所對(duì)應(yīng)的WindowToken對(duì)象,接著再將該WindowToken對(duì)象從WindowManagerService類(lèi)的成員變量mTokenMap和mTokenList中刪除。
刪除了一個(gè)WindowToken對(duì)象之后,如果該WindowToken對(duì)象不是處于不可見(jiàn)的狀態(tài),即它的成員變量hidden的值不等于false,那么就意味著它所描述窗口口也有可能是可見(jiàn)的,那么WindowManagerService類(lèi)的成員函數(shù)removeWindowToken就需要作以下兩個(gè)檢查:
A. 如果該WindowToken對(duì)象所描述的窗口的其中一個(gè)處于動(dòng)畫(huà)顯示過(guò)程,即用來(lái)描述該窗口的一個(gè)WindowState對(duì)象的成員函數(shù)isAnimating的返回值等于true,那么就需要該WindowToken對(duì)象的狀態(tài)設(shè)置為正在退出狀態(tài),即將它保存在WindowManagerService類(lèi)的成員變量mExitingTokens所描述的一個(gè)ArrayList中。
B. 如果該WindowToken對(duì)象所描述的窗口是可見(jiàn)的,即用來(lái)描述該窗口的一個(gè)WindowState對(duì)象的成員函數(shù)isVisibleNow的返回值等于true,那么就需要調(diào)用WindowManagerService類(lèi)的成員函數(shù)applyAnimationLocked來(lái)給它應(yīng)用一個(gè)退出動(dòng)畫(huà),該退出動(dòng)畫(huà)是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)performLayoutAndPlaceSurfacesLocked來(lái)實(shí)現(xiàn)的。當(dāng)一個(gè)窗口退出了之后,系統(tǒng)當(dāng)前獲得焦點(diǎn)的窗口可能會(huì)發(fā)生變化,這時(shí)候就需要調(diào)用WindowManagerService類(lèi)的成員函數(shù)updateFocusedWindowLocked來(lái)重新調(diào)整系統(tǒng)當(dāng)前獲得焦點(diǎn)的窗口。
注意,如果正在刪除的WindowToken對(duì)象是用來(lái)描述壁紙窗口的,那么還需要將該WindowToken對(duì)象從WindowManagerService類(lèi)的成員變量mWallpaperTokens所描述的一個(gè)ArrayList中刪除。
8. 增加WindowState
從前面Android應(yīng)用程序窗口(Activity)與WindowManagerService服務(wù)的連接過(guò)程分析一文可以知道,增加一個(gè)窗口WindowManagerService服務(wù)最終是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)addWindow來(lái)實(shí)現(xiàn)的,如下所示:
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
/**
* Mapping from an IWindow IBinder to the server's Window object.
* This is also used as the lock for all of our state.
*/
final HashMap<IBinder, WindowState> mWindowMap = new HashMap<IBinder, WindowState>();
......
/**
* Z-ordered (bottom-most first) list of all Window objects.
*/
final ArrayList<WindowState> mWindows = new ArrayList<WindowState>();
......
public int addWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int viewVisibility,
Rect outContentInsets, InputChannel outInputChannel) {
......
WindowState win = null;
synchronized(mWindowMap) {
......
win = new WindowState(session, client, token,
attachedWindow, attrs, viewVisibility);
......
mWindowMap.put(client.asBinder(), win);
......
if (attrs.type == TYPE_INPUT_METHOD) {
mInputMethodWindow = win;
addInputMethodWindowToListLocked(win);
......
} else if (attrs.type == TYPE_INPUT_METHOD_DIALOG) {
mInputMethodDialogs.add(win);
addWindowToListInOrderLocked(win, true);
adjustInputMethodDialogsLocked();
......
} else {
addWindowToListInOrderLocked(win, true);
if (attrs.type == TYPE_WALLPAPER) {
.......
adjustWallpaperWindowsLocked();
} else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
adjustWallpaperWindowsLocked();
}
}
......
}
......
}
......
}
這個(gè)函數(shù)定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
WindowManagerService類(lèi)有兩個(gè)成員變量mWindowMap和mWindows是用來(lái)保存系統(tǒng)中的WindowState對(duì)象。其中,成員變量mWindowMap指向的是一個(gè)HashMap,它的關(guān)鍵字是一個(gè)IBinder接口,一般這個(gè)IBinder接口指向的是一個(gè)Binder代理對(duì)象,引用了運(yùn)行在應(yīng)用程序進(jìn)程這一側(cè)的一個(gè)類(lèi)型為W的Binder本地對(duì)象,用來(lái)描述一個(gè)窗口;成員變量mWindows指向的是一個(gè)ArrayList,保存在它里面的WindowState對(duì)象是按照其Z軸位置從小到大的順序排列的。成員變量mWindowMap和mWindows的區(qū)別在于,前者給在定一個(gè)IBinder接口的情況下,可以快速找到與對(duì)應(yīng)的WindowState對(duì)象,而后者用來(lái)從上到下或者下到上遍歷系統(tǒng)的WindowState對(duì)象。由于系統(tǒng)中的WindowState對(duì)象是按照其Z軸位置從小到大的順序排列在成員變量mWindows中的,因此,成員變量mWindows所指向的ArrayList就是我們?cè)谇懊鎴D1中所說(shuō)的Window Stack。
理解了WindowManagerService類(lèi)有兩個(gè)成員變量mWindowMap和mWindows的作用之后,WindowManagerService類(lèi)的成員函數(shù)addWindow增加一個(gè)WindowState對(duì)象的過(guò)程就容易理解了。
參數(shù)client是一個(gè)Binder代理對(duì)象,引用了運(yùn)行在應(yīng)用程序進(jìn)程這一側(cè)的一個(gè)類(lèi)型為W的Binder本地對(duì)象,用來(lái)描述要增加到WindowManagerService服務(wù)中的一個(gè)窗口。WindowManagerService類(lèi)的成員函數(shù)addWindow首先創(chuàng)建一個(gè)WindowState對(duì)象win,接著再以參數(shù)client所描述的一個(gè)Binder代理對(duì)象的IBinder接口為關(guān)鍵字,將WindowState對(duì)象win保存在WindowManagerService類(lèi)的成員變量mWindowMap中,最后還會(huì)根據(jù)要增加到WindowManagerService服務(wù)中的窗口的類(lèi)型來(lái)調(diào)用不同的成員函數(shù)將WindowState對(duì)象win增加到WindowManagerService類(lèi)的成員變量mWindows中:
A. 如果要增加的是輸入法窗口,即參數(shù)attrs所描述的一個(gè)WindowManager.LayoutParams對(duì)象的成員變量type的值等于TYPE_INPUT_METHOD,那么就會(huì)調(diào)用成員函數(shù)addInputMethodWindowToListLocked來(lái)將WindowState對(duì)象win增加到WindowManagerService類(lèi)的成員變量mWindows中去,并且會(huì)將WindowState對(duì)象win保存在WindowManagerService類(lèi)的成員變量mInputMethodWindow中。
B. 如果要增加的是輸入法對(duì)話(huà)框,即參數(shù)attrs所描述的一個(gè)WindowManager.LayoutParams對(duì)象的成員變量type的值等于TYPE_INPUT_METHOD_DIALOG,那么就會(huì)調(diào)用成員函數(shù)addWindowToListInOrderLocked來(lái)將WindowState對(duì)象win增加到WindowManagerService類(lèi)的成員變量mWindows中去,并且會(huì)將WindowState對(duì)象win保存在WindowManagerService類(lèi)的成員變量mInputMethodDialogs中,以及調(diào)用成員函數(shù)adjustInputMethodDialogsLocked來(lái)調(diào)整剛才所添加的輸入法窗口在窗口堆棧中的位置,使得它位于系統(tǒng)當(dāng)前需要輸入法窗口的窗口的上面。
C. 如果要增加的是壁紙窗口,即參數(shù)attrs所描述的一個(gè)WindowManager.LayoutParams對(duì)象的成員變量type的值等于TYPE_WALLPAPER,那么就會(huì)調(diào)用成員函數(shù)addWindowToListInOrderLocked來(lái)將WindowState對(duì)象win增加到WindowManagerService類(lèi)的成員變量mWindows中去,并且會(huì)調(diào)用成員函數(shù)adjustWallpaperWindowsLocked來(lái)調(diào)整剛才所添加的壁紙窗口在窗口堆棧中的位置,使得它位于系統(tǒng)當(dāng)前需要壁紙窗口的窗口的下面。
D . 如果要增加的既不是輸入法窗口,也不是輸入法對(duì)話(huà)框和壁紙窗口,那么就只會(huì)調(diào)用成員函數(shù)addWindowToListInOrderLocked來(lái)將WindowState對(duì)象win增加到WindowManagerService類(lèi)的成員變量mWindows中去,但是如果要增加的窗口需要顯示壁紙,即參數(shù)attrs所描述的一個(gè)WindowManager.LayoutParams對(duì)象的成員變量flags的FLAG_SHOW_WALLPAPER位等于1,那么還會(huì)繼續(xù)調(diào)用成員函數(shù)adjustWallpaperWindowsLocked來(lái)調(diào)整系統(tǒng)中的壁紙窗口在窗口堆棧中的位置,使得它位于剛才所添加的窗口的下面。
在后面的兩篇文章中,我們?cè)僭敿?xì)分析WindowManagerService類(lèi)的成員函數(shù)addInputMethodWindowToListLocked、adjustInputMethodDialogsLocked和adjustWallpaperWindowsLocked的實(shí)現(xiàn),其中,前兩者是與輸入法窗口相關(guān)的,而后者是與壁紙窗口相關(guān)的。本文主要關(guān)注WindowManagerService類(lèi)的成員函數(shù)addWindowToListInOrderLocked的實(shí)現(xiàn),它會(huì)將一個(gè)指定的WindowState對(duì)象增加到窗口堆棧中的合適位置上去。
9. 增加WindowState到窗口堆棧
從前面的分析可以知道,將一個(gè)WindowState對(duì)象增加到WindowManagerService服務(wù)內(nèi)部中的窗口堆棧,即WindowManagerService類(lèi)的成員變量mWindows,是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)addWindowToListInOrderLocked來(lái)實(shí)現(xiàn)的。
WindowManagerService類(lèi)的成員函數(shù)addWindowToListInOrderLocked的實(shí)現(xiàn)比較復(fù)雜,我們先列出它的框架,然后再詳細(xì)分析它的實(shí)現(xiàn),如下所示:
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
private void addWindowToListInOrderLocked(WindowState win, boolean addToToken) {
final IWindow client = win.mClient;
final WindowToken token = win.mToken;
final ArrayList<WindowState> localmWindows = mWindows;
final int N = localmWindows.size();
final WindowState attached = win.mAttachedWindow;
int i;
if (attached == null) {
//CASE 1:要增加的窗口win沒(méi)有附加在其它窗口上
int tokenWindowsPos = token.windows.size();
if (token.appWindowToken != null) {
//CASE 1.1:要增加的窗口win是一個(gè)Activity窗口
int index = tokenWindowsPos-1;
if (index >= 0) {
//CASE 1.1.1:用來(lái)要增加的窗口win的令牌token已存在其它窗口
......
} else {
//CASE 1.1.2:用來(lái)要增加的窗口win的令牌token尚未存在任何窗口
......
}
} else {
//CASE 1.2:要增加的窗口win不是一個(gè)Activity窗口
......
}
if (addToToken) {
token.windows.add(tokenWindowsPos, win);
}
} else {
//CASE 2:要增加的窗口win附加在窗口attached上
......
}
if (win.mAppToken != null && addToToken) {
win.mAppToken.allAppWindows.add(win);
}
}
......
}
這個(gè)函數(shù)定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
我們首先分析一下WindowManagerService類(lèi)的成員函數(shù)addWindowToListInOrderLocked的幾個(gè)本地變量的含義:
A. token。本地變量token指向的是參數(shù)win所描述的一個(gè)WindowState對(duì)象的成員變量mToken所指向一個(gè)WindowToken對(duì)象,這個(gè)WindowToken對(duì)象用來(lái)描述WindowState對(duì)象win所對(duì)應(yīng)的窗口令牌。
B. localmWindows。本地變量localmWindows指向的是WindowManagerService類(lèi)的成員變量mWindows所描述的一個(gè)ArrayList,即一個(gè)窗口堆棧,WindowManagerService類(lèi)的成員函數(shù)addWindowToListInOrderLocked的目標(biāo)就是要將參數(shù)win所描述的一個(gè)WindowState對(duì)象增加到該窗口堆棧的合適位置上去。
C. attached。本地變量attached指向的是參數(shù)win所描述的一個(gè)WindowState對(duì)象的成員變量mAttachedWindow 所指向的一個(gè)WindowState對(duì)象,如果它的值不等于null,那么就意味參數(shù)win所描述的窗口要附加在本地變量attached所描述的窗口上。
D. tokenWindowsPos。本地變量tokenWindowsPos用來(lái)描述與窗口令牌token所對(duì)應(yīng)的窗口的數(shù)量。
E. token.appWindowToken。從前面Android應(yīng)用程序窗口(Activity)與WindowManagerService服務(wù)的連接過(guò)程分析一文可以知道,如果一個(gè)WindowToken對(duì)象的成員變量appWindowToken的值不等于null,那么就意味著該WindowToken對(duì)象的實(shí)際類(lèi)型為是AppWindowToken,即它所描述的是一個(gè)Activity窗口令牌,這種類(lèi)型的令牌的特點(diǎn)是在ActivityManagerService服務(wù)的Activity組件堆棧中對(duì)應(yīng)有一個(gè)ActivityRecord對(duì)象,如圖1所示。
F. index。本地變量index的值等于tokenWindowsPos-1,如果它的值大于等于0,那么就意味著窗口令牌tokent已經(jīng)存在其它窗口,否則的話(huà),就意味著窗口令牌tokent尚未存在任何窗口。
從這些本地變量的含義,我們就可以分情況來(lái)將參數(shù)win所描述的一個(gè)WindowState對(duì)象增加到WindowManagerService服務(wù)內(nèi)部的窗口堆棧的合適位置上去:
CASE 1:要增加的窗口win沒(méi)有附加在其它窗口上
----CASE 1.1:要增加的窗口win是一個(gè)Activity窗口
----CASE 1.1.1:用來(lái)要增加的窗口win的令牌token已存在其它窗口。這時(shí)候意味著窗口win需要保存在其它已經(jīng)存在的窗口的附近,因此,我們只要找到這些已經(jīng)存在的窗口在窗口堆棧中的位置,那么再根據(jù)其它屬性,就可以將窗口win保存在已經(jīng)存在的窗口的上面或者下面。
----CASE 1.1.2:用來(lái)要增加的窗口win的令牌token尚未存在任何窗口。雖然這時(shí)候窗口win在窗口堆棧中沒(méi)有位置可以參考,但是它畢竟是一個(gè)Activity窗口,我們可以通過(guò)與它所對(duì)應(yīng)的AppWindowToken對(duì)象在App Token List(即WindowManagerService類(lèi)的成員變量mAppTokens所描述的一個(gè)ArrayList)中的位置來(lái)獲得它窗口堆棧中的位置?;貞浳覀?cè)谇懊娴?節(jié)分析移動(dòng)AppWindowToken至指定位置的操作時(shí)得到的結(jié)論:WindowManagerService服務(wù)內(nèi)部中的所有WindowState對(duì)象都是按照Z(yǔ)軸從位置從小到大排列在WindowState堆棧中的,并且在mAppTokens列表中,位于上面的一個(gè)AppWindowToken對(duì)象所對(duì)應(yīng)的那些WindowState對(duì)象的Z軸位置是一定大于位于下面的一個(gè)AppWindowToken對(duì)象所對(duì)應(yīng)的那些WindowState對(duì)象的Z軸位置的。因此,我們只要找到用來(lái)描述窗口win的一個(gè)AppWindowToken對(duì)象(token.appWindowToken)的上一個(gè)或者下一個(gè)AppWindowToken對(duì)象所對(duì)應(yīng)的窗口在窗口堆棧中的位置,那么就可以這個(gè)位置為參考,得到窗口win在窗口堆棧中的位置。
----CASE 1.2:要增加的窗口win不是一個(gè)Activity窗口。這時(shí)候既然要增加的窗口也沒(méi)有附加在其它窗口上,那么就意味著要增加的窗口win在窗口堆棧中沒(méi)有位置可以參考,因此,我們就需要根據(jù)它的Z軸位置來(lái)決定它在窗口堆棧的位置。
CASE 2:要增加的窗口win附加在窗口attached上。這時(shí)候就意味著要增加的窗口win要保存在窗口attached的上面,即窗口在窗口堆棧的位置要以窗口attached在窗口堆棧的位置為參考。
從上面的分析就可以知道,CASE 1.1.1、CASE 1.1.2和CASE 2都有一個(gè)共同特點(diǎn),即要增加的窗口win在窗口堆棧的位置有一個(gè)參考值,而在CASE 1.2中,要增加的窗口win在窗口堆棧的位置沒(méi)有參考值,需要通過(guò)其Z軸位置來(lái)確定。
在分析上述四種情況之前, 我們還需要再說(shuō)明一下WindowManagerService類(lèi)的成員函數(shù)addWindowToListInOrderLocked的參數(shù)addToToken的含義。參數(shù)addToToken是一個(gè)布爾變量,如果它的值等于true,那么就說(shuō)明需要將參數(shù)win所描述的一個(gè)WindowState對(duì)象添加用來(lái)描述它的窗口令牌token的成員變量windows所描述的一個(gè)ArrayList中去。注意,窗口令牌token的成員變量windows所描述的一個(gè)ArrayList里面所保存的WindowState對(duì)象是按照Z(yǔ)軸位置從小到大的順序來(lái)排列的,因此,在將WindowState對(duì)象win保存到這個(gè)ArrayList之前,首先要按照它的Z軸位置計(jì)算得到它在這個(gè)ArrayList中的位置tokenWindowsPos。另一方面,在參數(shù)addToToken的值等于true,并且參數(shù)win所描述的是一個(gè)Activity窗口,即它的成員變量mAppToken不等于null的情況下,還需要將參數(shù)win所描述的一個(gè)WindowState對(duì)象保存在用來(lái)描述它的窗口令牌,即一個(gè)AppWindowToken對(duì)象成員變量allAppWindows所描述的一個(gè)ArrayList中去,以便可以知道一個(gè)AppWindowToken對(duì)象對(duì)應(yīng)的Activity窗口都有哪些。
接下來(lái),我們就分別分析這四種情況是如何將窗口win增加窗口堆棧中去的。
CASE 1.1.1對(duì)應(yīng)的代碼為:
if (win.mAttrs.type == TYPE_BASE_APPLICATION) {
// Base windows go behind everything else.
placeWindowBefore(token.windows.get(0), win);
tokenWindowsPos = 0;
} else {
AppWindowToken atoken = win.mAppToken;
if (atoken != null &&
token.windows.get(index) == atoken.startingWindow) {
placeWindowBefore(token.windows.get(index), win);
tokenWindowsPos--;
} else {
int newIdx = findIdxBasedOnAppTokens(win);
if(newIdx != -1) {
//there is a window above this one associated with the same
//apptoken note that the window could be a floating window
//that was created later or a window at the top of the list of
//windows associated with this token.
......
localmWindows.add(newIdx+1, win);
mWindowsChanged = true;
}
}
}
這段代碼又分為三種情況來(lái)將參數(shù)win所描述的一個(gè)WindowState對(duì)象添加到窗口堆棧中:
A. 參數(shù)win描述的窗口的類(lèi)型為T(mén)YPE_BASE_APPLICATION。在一個(gè)令牌對(duì)應(yīng)的所有窗口中,類(lèi)型為T(mén)YPE_BASE_APPLICATION的窗口位于其它類(lèi)型的窗口的下面。因此,這段代碼就會(huì)調(diào)用WindowManagerService類(lèi)的成員函數(shù)placeWindowBefore來(lái)將參數(shù)win所描述的一個(gè)WindowState對(duì)象保存窗口堆棧中,并且它是位于令牌token的窗口列表的第0個(gè)位置的WindowState對(duì)象的下面。這時(shí)候變量tokenWindowsPos的值會(huì)被設(shè)置為0,表示參數(shù)win所描述的一個(gè)WindowState對(duì)象要保存窗口令牌token的窗口列表的第0個(gè)位置上。
B. 參數(shù)win描述的一個(gè)WindowState對(duì)象的成員變量mAppToken的值不等于null,這意味著參數(shù)win描述的是一個(gè)Activity窗口,這時(shí)候如果窗口令牌atoken(與token描述的是同一個(gè)窗口令牌)的窗口列表的第index個(gè)位置(即最上面的一個(gè)位置) 的WindowState對(duì)象描述的是一個(gè)Activity啟動(dòng)窗口,即與窗口令牌atoken的成員變量startingWindow描述的是同一個(gè)窗口,那么就說(shuō)明窗口令牌atoken的窗口列表的第index個(gè)位置的WindowState對(duì)象描述的是窗口win的啟動(dòng)窗口。由于一個(gè)窗口的啟動(dòng)窗口總是位于它的上面,因此,這段代碼就會(huì)調(diào)用WindowManagerService類(lèi)的成員函數(shù)placeWindowBefore來(lái)將參數(shù)win所描述的一個(gè)WindowState對(duì)象保存窗口堆棧中,并且它是位于令牌atoken的窗口列表的第index個(gè)位置的WindowState對(duì)象的下面。這時(shí)候變量tokenWindowsPos的值減少1,即相當(dāng)于是等于index,表示參數(shù)win所描述的一個(gè)WindowState對(duì)象要插入在窗口令牌token的窗口列表的第index個(gè)位置上。
C. 參數(shù)win所描述的窗口的類(lèi)型既不是TYPE_BASE_APPLICATION,而且它也沒(méi)有啟動(dòng)窗口,那么這時(shí)候就需要將它保存在窗口令牌token的窗口列表的最上面一個(gè)窗口的上面。窗口令牌token的窗口列表的最上面一個(gè)窗口在窗口堆棧中的位置newIdx是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)findIdxBaseOnAppTokens來(lái)獲得的,這時(shí)候參數(shù)win所描述的一個(gè)WindowState對(duì)象就應(yīng)該保存在窗口堆棧,即變量localmWindows所描述的一個(gè)ArrayList的第newIdx+1個(gè)位置上。
CASE 1.1.2對(duì)應(yīng)的代碼為:
// Figure out where the window should go, based on the
// order of applications.
final int NA = mAppTokens.size();
WindowState pos = null;
for (i=NA-1; i>=0; i--) {
AppWindowToken t = mAppTokens.get(i);
if (t == token) {
i--;
break;
}
// We haven't reached the token yet; if this token
// is not going to the bottom and has windows, we can
// use it as an anchor for when we do reach the token.
if (!t.sendingToBottom && t.windows.size() > 0) {
pos = t.windows.get(0);
}
}
// We now know the index into the apps. If we found
// an app window above, that gives us the position; else
// we need to look some more.
if (pos != null) {
// Move behind any windows attached to this one.
WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
if (atoken != null) {
final int NC = atoken.windows.size();
if (NC > 0) {
WindowState bottom = atoken.windows.get(0);
if (bottom.mSubLayer < 0) {
pos = bottom;
}
}
}
placeWindowBefore(pos, win);
} else {
// Continue looking down until we find the first
// token that has windows.
while (i >= 0) {
AppWindowToken t = mAppTokens.get(i);
final int NW = t.windows.size();
if (NW > 0) {
pos = t.windows.get(NW-1);
break;
}
i--;
}
if (pos != null) {
// Move in front of any windows attached to this
// one.
WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
if (atoken != null) {
final int NC = atoken.windows.size();
if (NC > 0) {
WindowState top = atoken.windows.get(NC-1);
if (top.mSubLayer >= 0) {
pos = top;
}
}
}
placeWindowAfter(pos, win);
placeWindowAfter(pos, win);
} else {
// Just search for the start of this layer.
final int myLayer = win.mBaseLayer;
for (i=0; i<N; i++) {
WindowState w = localmWindows.get(i);
if (w.mBaseLayer > myLayer) {
break;
}
}
......
localmWindows.add(i, win);
mWindowsChanged = true;
}
}
這段代碼要能冠軍WindowManagerService服務(wù)內(nèi)部的一個(gè)AppWindowToken列表mAppTokens來(lái)在窗口堆棧中找到一個(gè)參數(shù)位置來(lái)保存參數(shù)win所描述的一個(gè)WindowState對(duì)象。
最上面的一個(gè)for循環(huán)執(zhí)行完成之后,我們假設(shè)變量pos的值不等于null,這時(shí)候它與變量i以及變量token的關(guān)系如圖2所示:
圖2 窗口win位于窗口C的下面
這時(shí)候位于令牌token上面的令牌在窗口堆棧中對(duì)應(yīng)有WindowState對(duì)象。注意,這時(shí)候第i+2個(gè)令牌在窗口堆棧中不對(duì)應(yīng)有WindowState對(duì)象,而第i+3個(gè)令牌在窗口堆棧中對(duì)應(yīng)有C和D兩個(gè)WindowState對(duì)象,并且這兩個(gè)WindowState對(duì)象所描述的窗口都不是即將要切換到窗口堆棧的底部的。由于第i+3個(gè)令牌位于令牌token的上面,并且這兩個(gè)令牌之間的其它令牌在窗口堆棧中不對(duì)應(yīng)有WindowState對(duì)象,因此,這時(shí)候參數(shù)win所描述的WindowState對(duì)象在窗口堆棧中的位置應(yīng)該以第i+3個(gè)令牌所對(duì)應(yīng)的Z軸位置最小的WindowState對(duì)象在窗口堆棧中的位置為參考,即以WindowState對(duì)象C在窗口堆棧中的位置為參考,而WindowState對(duì)象C也正好是變量pos所指向的WindowState對(duì)象。
接下來(lái),上述代碼會(huì)繼續(xù)檢查WindowState對(duì)象C是否附加有SubLayer值小于0的窗口。如果有的話(huà),那么就會(huì)將變量pos指向SubLayer值最小的那個(gè)WindowState對(duì)象,這是因?yàn)樵揥indowState對(duì)象是在WindowState對(duì)象C的最下面的,并且它與WindowState對(duì)象C是同屬一個(gè)令牌的。最后,上述代碼就會(huì)調(diào)用WindowManagerService類(lèi)的成員函數(shù)placeWindowBefore來(lái)將參數(shù)win所描述的一個(gè)WindowState對(duì)象保存窗口堆棧中由變量pos所指向的那個(gè)WindowState對(duì)象的下面。
假設(shè)最上面的一個(gè)for循環(huán)執(zhí)行完成之后,變量pos的值等于null,那么就說(shuō)明位于令牌token上面的令牌在窗口堆棧中都沒(méi)有對(duì)應(yīng)有WindowState對(duì)象,或者說(shuō)它們所對(duì)應(yīng)的WindowState對(duì)象都是即將要切換到窗口堆棧的底部去的,這時(shí)候就需要通過(guò)位于令牌token上面的令牌來(lái)在窗口堆棧中找到一個(gè)參考位置來(lái)保存參數(shù)win所描述的WindowState對(duì)象,這是通過(guò)中間的while循環(huán)來(lái)實(shí)現(xiàn)的。
中間的while循環(huán)執(zhí)行完成之后,假設(shè)變量pos的值不等于null,這時(shí)候它與變量i以及變量token的關(guān)系如圖3所示:

圖3 窗口win位于窗口D的上面
這時(shí)候位于令牌token上面的令牌在窗口堆棧中沒(méi)有對(duì)應(yīng)有WindowState對(duì)象。注意,這時(shí)候第i-1個(gè)令牌在窗口堆棧中不對(duì)應(yīng)有WindowState對(duì)象,而第i-2個(gè)令牌在窗口堆棧中對(duì)應(yīng)有C和D兩個(gè)WindowState對(duì)象。由于第i-2個(gè)令牌位于令牌token的下面,并且這兩個(gè)令牌之間的其它令牌在窗口堆棧中不對(duì)應(yīng)有WindowState對(duì)象,因此,這時(shí)候參數(shù)win所描述的WindowState對(duì)象在窗口堆棧中的位置應(yīng)該以第i-2個(gè)令牌所對(duì)應(yīng)的Z軸位置最大的WindowState對(duì)象在窗口堆棧中的位置為參考,即以WindowState對(duì)象D在窗口堆棧中的位置為參考,而WindowState對(duì)象D也正好是變量pos所指向的WindowState對(duì)象。
接下來(lái),上述代碼會(huì)繼續(xù)檢查WindowState對(duì)象D是否附加有SubLayer值大于等于0的窗口。如果有的話(huà),那么就會(huì)將變量pos指向SubLayer值最大的那個(gè)WindowState對(duì)象,這是因?yàn)樵揥indowState對(duì)象是在WindowState對(duì)象D的最上面的,并且它與WindowState對(duì)象D是同屬一個(gè)令牌的。最后,上述代碼就會(huì)調(diào)用WindowManagerService類(lèi)的成員函數(shù)placeWindowAfter來(lái)將參數(shù)win所描述的一個(gè)WindowState對(duì)象保存窗口堆棧中由變量pos所指向的那個(gè)WindowState對(duì)象的上面。
假設(shè)中間的while循環(huán)執(zhí)行完成之后,變量pos的值等于null,這時(shí)候就說(shuō)明在窗口堆棧中實(shí)在是找不到參考位置來(lái)保存參數(shù)win所描述的WindowState對(duì)象了,因此,就只能通過(guò)參數(shù)win所描述的WindowState對(duì)象的Z軸位置,即它的成員變量mBaseLayer的值來(lái)在窗口堆棧中找到一個(gè)合適的位置了,如最下面的for循環(huán)所示。由于窗口堆棧中的WindowState對(duì)象是按照它們的Z軸位置由小到大的順序來(lái)排列的,因此,最下面的for循環(huán)只要從下到上找到一個(gè)Z軸位置比參數(shù)win所描述的WindowState對(duì)象的Z軸位置大的一個(gè)WindowState對(duì)象在窗口堆棧中的位置i,那么就可以將參數(shù)win所描述的WindowState對(duì)象插入在窗口堆棧的第i個(gè)位置上了。
CASE 1.2對(duì)應(yīng)的代碼為:
// Figure out where window should go, based on layer.
final int myLayer = win.mBaseLayer;
for (i=N-1; i>=0; i--) {
if (localmWindows.get(i).mBaseLayer <= myLayer) {
i++;
break;
}
}
if (i < 0) i = 0;
......
localmWindows.add(i, win);
mWindowsChanged = true;
由于這時(shí)候在窗口堆棧中是沒(méi)有參考位置來(lái)保存參數(shù)win所描述的WindowState對(duì)象的,因此,這段代碼就只能通過(guò)參數(shù)win所描述的WindowState對(duì)象的Z軸位置,即它的成員變量mBaseLayer的值來(lái)在窗口堆棧中找到一個(gè)合適的位置了,如這段代碼中的for循環(huán)所示。由于窗口堆棧中的WindowState對(duì)象是按照它們的Z軸位置由小到大的順序來(lái)排列的,因此,這段代碼中的for循環(huán)只要從上到下找到一個(gè)WindowState對(duì)象,它的Z軸位置小于或者等于參數(shù)win所描述的WindowState對(duì)象的Z軸位置,那么該WindowState對(duì)象在窗口堆棧中的位置i就可以用插入?yún)?shù)win所描述的WindowState對(duì)象了。
CASE 2對(duì)應(yīng)的代碼為:
// Figure out this window's ordering relative to the window
// it is attached to.
final int NA = token.windows.size();
final int sublayer = win.mSubLayer;
int largestSublayer = Integer.MIN_VALUE;
WindowState windowWithLargestSublayer = null;
for (i=0; i<NA; i++) {
WindowState w = token.windows.get(i);
final int wSublayer = w.mSubLayer;
if (wSublayer >= largestSublayer) {
largestSublayer = wSublayer;
windowWithLargestSublayer = w;
}
if (sublayer < 0) {
// For negative sublayers, we go below all windows
// in the same sublayer.
if (wSublayer >= sublayer) {
if (addToToken) {
token.windows.add(i, win);
}
placeWindowBefore(
wSublayer >= 0 ? attached : w, win);
break;
}
} else {
// For positive sublayers, we go above all windows
// in the same sublayer.
if (wSublayer > sublayer) {
if (addToToken) {
token.windows.add(i, win);
}
placeWindowBefore(w, win);
break;
}
}
}
if (i >= NA) {
if (addToToken) {
token.windows.add(win);
}
if (sublayer < 0) {
placeWindowBefore(attached, win);
} else {
placeWindowAfter(largestSublayer >= 0
? windowWithLargestSublayer
: attached,
win);
}
}
這段代碼要將參數(shù)win所描述的WindowState對(duì)象附加在變量attached所描述的WindowState對(duì)象的上面或者下面,取決于它的成員變量mSubLayer的值是大于0還是小于0。我們分四種情況來(lái)考慮。
第一種情況是參數(shù)win所描述的WindowState對(duì)象的成員變量mSubLayer的值小于0,并且這時(shí)候在附加在窗口attached的WindowState對(duì)象中,存在一個(gè)WindowState對(duì)象,它的成員變量mSubLayer的值大于等于參數(shù)win所描述的WindowState對(duì)象的成員變量mSubLayer的值,如圖4和圖5所示:
圖4 窗口win插入到窗口B的下面
圖5 窗口win插入在窗口attached的下面
在圖4和圖5中,WindowState對(duì)象A和B均是附加在WindowState對(duì)象attached中。
在圖4中,WindowState對(duì)象A和B的成員變量mSubLayer的值均小于0,而WindowState對(duì)象win的成員變量mSubLayer的值比WindowState對(duì)象A的大,但是比WindowState對(duì)象B的小,這時(shí)候WindowState對(duì)象win在窗口堆棧中就應(yīng)該位于WindowState對(duì)象B的下面,這是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)placeWindowBefore來(lái)實(shí)現(xiàn)的。
在圖5中,WindowState對(duì)象A和B的成員變量mSubLayer的值均大于0,由于WindowState對(duì)象win的成員變量mSubLayer的值小于0,這時(shí)候WindowState對(duì)象win在窗口堆棧中就應(yīng)該位于WindowState對(duì)象attached的下面,這是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)placeWindowBefore來(lái)實(shí)現(xiàn)的。
第二種情況是參數(shù)win所描述的WindowState對(duì)象的成員變量mSubLayer的值大于0,并且這時(shí)候在附加在窗口attached的WindowState對(duì)象中,存在一個(gè)WindowState對(duì)象,它的成員變量mSubLayer的值大于參數(shù)win所描述的WindowState對(duì)象的成員變量mSubLayer的值,如圖6所示:
圖6 窗口win插入在窗口B的下面
在圖6中,WindowState對(duì)象A和B均是附加在WindowState對(duì)象attached中。其中,WindowState對(duì)象A和B的成員變量mSubLayer的值均大于0,而WindowState對(duì)象win的成員變量mSubLayer的值比WindowState對(duì)象A的大,但是比WindowState對(duì)象B的小,這時(shí)候WindowState對(duì)象win在窗口堆棧中就應(yīng)該位于WindowState對(duì)象B的下面,這是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)placeWindowBefore來(lái)實(shí)現(xiàn)的。
第三種情況是參數(shù)win所描述的WindowState對(duì)象的成員變量mSubLayer的值小于0,但是在附加在窗口attached的WindowState對(duì)象中,找不到一個(gè)WindowState對(duì)象,它的成員變量mSubLayer的值比WindowState對(duì)象的成員變量mSubLayer的值大,如圖7所示:
圖7 窗口win插入在窗口attached的下面
在圖7中,WindowState對(duì)象A和B均是附加在WindowState對(duì)象attached中。其中,WindowState對(duì)象A和B以及win的成員變量mSubLayer的值均小于0,但是WindowState對(duì)象win的成員變量mSubLayer的值比WindowState對(duì)象A和B的都要大,這時(shí)候WindowState對(duì)象win在窗口堆棧中就應(yīng)該位于WindowState對(duì)象attached的下面,這是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)placeWindowBefore來(lái)實(shí)現(xiàn)的。
第四種情況是參數(shù)win所描述的WindowState對(duì)象的成員變量mSubLayer的值大于等于0,但是在附加在窗口attached的WindowState對(duì)象中,找不到一個(gè)WindowState對(duì)象,它的成員變量mSubLayer的值比WindowState對(duì)象的成員變量mSubLayer的值大,如圖8和圖9所示:
圖8 窗口win插入在窗口B的上面
圖9 窗口win插入在窗口attached的上面
在圖8和圖9中,WindowState對(duì)象A和B均是附加在WindowState對(duì)象attached中。
在圖8中,WindowState對(duì)象A和B的成員變量mSubLayer的值均大于0,并且WindowState對(duì)象win的成員變量mSubLayer的值比WindowState對(duì)象A和B的都要大,這時(shí)候WindowState對(duì)象win在窗口堆棧中就應(yīng)該位于WindowState對(duì)象B的上面,這是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)placeWindowAfter來(lái)實(shí)現(xiàn)的。
在圖9中,WindowState對(duì)象A和B的成員變量mSubLayer的值均小于等于0,而WindowState對(duì)象win的成員變量mSubLayer的值大于0,這時(shí)候WindowState對(duì)象win在窗口堆棧中就應(yīng)該位于WindowState對(duì)象attached的上面,這是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)placeWindowAfter來(lái)實(shí)現(xiàn)的。
注意,在這四種情況中,如果參數(shù)addToToken的值等于true,那么都需要將參數(shù)win所描述的WindowState對(duì)象增加到與它所對(duì)應(yīng)的窗口令牌token的窗口列表windows中去。
10. 刪除WindowState
刪除WindowState是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)tmpRemoveWindowLocked來(lái)實(shí)現(xiàn)的,如下所示:
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
private int tmpRemoveWindowLocked(int interestingPos, WindowState win) {
int wpos = mWindows.indexOf(win);
if (wpos >= 0) {
if (wpos < interestingPos) interestingPos--;
......
mWindows.remove(wpos);
mWindowsChanged = true;
int NC = win.mChildWindows.size();
while (NC > 0) {
NC--;
WindowState cw = win.mChildWindows.get(NC);
int cpos = mWindows.indexOf(cw);
if (cpos >= 0) {
if (cpos < interestingPos) interestingPos--;
......
mWindows.remove(cpos);
}
}
}
return interestingPos;
}
......
}
這個(gè)函數(shù)定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
WindowManagerService類(lèi)的成員函數(shù)tmpRemoveWindowLocked將參數(shù)win所描述的窗口及其子窗口從WindowManagerService服務(wù)內(nèi)部的窗口堆棧中刪除,即從 WindowManagerService類(lèi)的成員變量mWindows所描述的一個(gè)ArrayList中刪除。
如果每一個(gè)被刪除的窗口在窗口堆棧中的位置比參數(shù)interestingPos的值小,那么WindowManagerService類(lèi)的成員函數(shù)tmpRemoveWindowLocked還會(huì)將參數(shù)interestingPos的值減少1,這相當(dāng)于是計(jì)算當(dāng)刪除參數(shù)win所描述的窗口及其子窗口之后,原來(lái)位于窗口堆棧中第interestingPos個(gè)位置的窗口現(xiàn)在位于窗口堆棧的位置,這個(gè)位置最終會(huì)作為WindowManagerService類(lèi)的成員函數(shù)tmpRemoveWindowLocked的返回值。
11. 在指定位置增加WindowState
在指定位置增加WindowState是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)reAddWindowLocked來(lái)實(shí)現(xiàn)的,如下所示:
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
private final int reAddWindowLocked(int index, WindowState win) {
final int NCW = win.mChildWindows.size();
boolean added = false;
for (int j=0; j<NCW; j++) {
WindowState cwin = win.mChildWindows.get(j);
if (!added && cwin.mSubLayer >= 0) {
......
mWindows.add(index, win);
index++;
added = true;
}
......
mWindows.add(index, cwin);
index++;
}
if (!added) {
......
mWindows.add(index, win);
index++;
}
mWindowsChanged = true;
return index;
}
......
}
這個(gè)函數(shù)定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
參數(shù)win描述的即為要增加的WindowState對(duì)象,而參數(shù)index描述的即為要將參數(shù)win所描述的WindowState對(duì)象及其子WindowState對(duì)象要增加到窗口堆棧中的起始位置。
由于參數(shù)win所描述的WindowState對(duì)象的子WindowState對(duì)象的成員變量mSubLayer的值可能會(huì)小于0,也可能大于0。大于0的子WindowState對(duì)象位于參數(shù)win所描述的WindowState對(duì)象的上面,而小于0的子WindowState對(duì)象位于參數(shù)win所描述的WindowState對(duì)象的下面。因此,WindowManagerService類(lèi)的成員函數(shù)reAddWindowLocked先增加那些小于0的子WindowState對(duì)象,接著再增加參數(shù)win所描述的WindowState對(duì)象,最后增加那些大于0的子WindowState對(duì)象。
假設(shè)WindowManagerService類(lèi)的成員函數(shù)reAddWindowLocked一共在窗口堆棧中增加了N個(gè)WindowState對(duì)象,那么它的返回值就等于index + N,這樣調(diào)用者就可以知道參數(shù)win所描述的WindowState對(duì)象及其子WindowState對(duì)象在窗口堆棧中的最高位置是多少。
基于第9、第10和第11這三操作,可以組合成很多其它的WindowState操作,如接下來(lái)的第12、第13、第14和第15個(gè)操作所示。
12. 將一個(gè)WindowState對(duì)象及其所有子WindowState對(duì)象增加到窗口堆棧中
將一個(gè)WindowState對(duì)象及其所有子WindowState對(duì)象增加到窗口堆棧中是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)reAddWindowToListInOrderLocked來(lái)實(shí)現(xiàn)的,如下所示:
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
private void reAddWindowToListInOrderLocked(WindowState win) {
addWindowToListInOrderLocked(win, false);
// This is a hack to get all of the child windows added as well
// at the right position. Child windows should be rare and
// this case should be rare, so it shouldn't be that big a deal.
int wpos = mWindows.indexOf(win);
if (wpos >= 0) {
......
mWindows.remove(wpos);
mWindowsChanged = true;
reAddWindowLocked(wpos, win);
}
}
......
}
這個(gè)函數(shù)定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
為了得到參數(shù)win所描述的WindowState對(duì)象的子WindowState對(duì)象在窗口堆棧中的起始位置,WindowManagerService類(lèi)的成員函數(shù)reAddWindowToListInOrderLocked首先將參數(shù)win所描述的WindowState對(duì)象增加到窗口堆棧中,這是通過(guò)調(diào)用前面所分析的成員函數(shù)addWindowToListInOrderLocked來(lái)實(shí)現(xiàn)的,目的是為了獲得它在窗口堆棧的位置。有了這個(gè)位置之后,WindowManagerService類(lèi)的成員函數(shù)reAddWindowToListInOrderLocked就可以調(diào)用前面所分析的成員函數(shù)reAddWindowLocked來(lái)將WindowState對(duì)象及其所有子WindowState對(duì)象增加到窗口堆棧中去了,不過(guò)在調(diào)用之前,要先將參數(shù)win所描述的WindowState對(duì)象從窗口中堆棧刪除。
13. 將一個(gè)WindowToken對(duì)象對(duì)應(yīng)的所有WindowState對(duì)象及其子WindowState對(duì)象增加到窗口堆棧的指定位置上
將一個(gè)WindowToken對(duì)象對(duì)應(yīng)的所有WindowState對(duì)象都增加到窗口堆棧中是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)reAddAppWindowsLocked來(lái)實(shí)現(xiàn)的,如下所示:
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
private final int reAddAppWindowsLocked(int index, WindowToken token) {
final int NW = token.windows.size();
for (int i=0; i<NW; i++) {
index = reAddWindowLocked(index, token.windows.get(i));
}
return index;
}
......
}
這個(gè)函數(shù)定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
與參數(shù)token所描述的WindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象保存在它的成員變量windows所描述的一個(gè)ArrayList中。通過(guò)遍歷這個(gè)ArrayList,就可以將與參數(shù)token所描述的WindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象及其子WindowState對(duì)象都增加到窗口堆棧的指定的起始位置上去,這是通過(guò)調(diào)用前面所分析的成員函數(shù)reAddWindowLocked來(lái)實(shí)現(xiàn)的。
參數(shù)index描述的便是最初指定的起始位置,每一次調(diào)用WindowManagerService類(lèi)的成員函數(shù)reAddWindowLocked之后,它的值都便會(huì)被更新為下一個(gè)WindowState對(duì)象及其子WindowState對(duì)象要增加到窗口堆棧中的位置。
最后,WindowManagerService類(lèi)的成員函數(shù)reAddAppWindowsLocked將與參數(shù)token所描述的WindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象在窗口堆棧中的最高位置加1后的得到結(jié)果返回給調(diào)用者。
14. 將一個(gè)AppWindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象及其子 WindowState對(duì)象移動(dòng)到窗口堆棧的指定位置上
將一個(gè)AppWindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象及其子 WindowState對(duì)象移動(dòng)到窗口堆棧的指定位置上是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)moveAppWindowsLocked來(lái)實(shí)現(xiàn)的,如下所示:
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
private void moveAppWindowsLocked(AppWindowToken wtoken, int tokenPos,
boolean updateFocusAndLayout) {
// First remove all of the windows from the list.
tmpRemoveAppWindowsLocked(wtoken);
// Where to start adding?
int pos = findWindowOffsetLocked(tokenPos);
// And now add them back at the correct place.
pos = reAddAppWindowsLocked(pos, wtoken);
if (updateFocusAndLayout) {
if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
assignLayersLocked();
}
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
}
......
}
這個(gè)函數(shù)定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
參數(shù)wtoken描述的是要移動(dòng)其所對(duì)應(yīng)的WindowState對(duì)象的一個(gè)AppWindowToken對(duì)象,而參數(shù)tokenPos描述的是該AppWindowToken對(duì)象在WindowManagerService服務(wù)內(nèi)部的AppWindowToken列表中的新位置。
WindowManagerService類(lèi)的成員函數(shù)moveAppWindowsLocked首先調(diào)用前面所分析的成員函數(shù)tmpRemoveAppWindowsLocked來(lái)移除所有與參數(shù)wtoken所描述的AppWindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象,接著再調(diào)用也是前面所分析的成員函數(shù)findWindowOffsetLocked來(lái)獲得與參數(shù)wtoken所描述的AppWindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象在窗口堆棧中的起始位置。有了這個(gè)起始位置之后,就可以也是前面所分析的成員函數(shù)reAddAppWindowsLocked來(lái)將與參數(shù)wtoken所描述的AppWindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象及其子WindowState對(duì)象移動(dòng)到窗口堆棧上去了。
最后,如果參數(shù)updateFocusAndLayout的值等于true,那么WindowManagerService類(lèi)的成員函數(shù)moveAppWindowsLocked還會(huì)更新系統(tǒng)當(dāng)前獲得焦點(diǎn)的窗口,以及重新計(jì)算系統(tǒng)中的所有窗口的Z軸位置以及重新布局系統(tǒng)中的所有窗口,這三個(gè)操作分別是通過(guò)調(diào)用WindowManagerService類(lèi)的成員函數(shù)updateFocusedWindowLocked、assignLayersLocked和performLayoutAndPlaceSurfacesLocked來(lái)實(shí)現(xiàn)的。
15. 將一組AppWindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象及其子 WindowState對(duì)象移動(dòng)到窗口堆棧的指定位置上
將一組AppWindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象及其子WindowState對(duì)象移動(dòng)到窗口堆棧的指定位置上是通過(guò)調(diào)用WindowManagerService類(lèi)的另外一個(gè)版本的成員函數(shù)moveAppWindowsLocked來(lái)實(shí)現(xiàn)的,如下所示:
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
private void moveAppWindowsLocked(List<IBinder> tokens, int tokenPos) {
// First remove all of the windows from the list.
final int N = tokens.size();
int i;
for (i=0; i<N; i++) {
WindowToken token = mTokenMap.get(tokens.get(i));
if (token != null) {
tmpRemoveAppWindowsLocked(token);
}
}
// Where to start adding?
int pos = findWindowOffsetLocked(tokenPos);
// And now add them back at the correct place.
for (i=0; i<N; i++) {
WindowToken token = mTokenMap.get(tokens.get(i));
if (token != null) {
pos = reAddAppWindowsLocked(pos, token);
}
}
if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
assignLayersLocked();
}
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
//dump();
}
......
}
這個(gè)函數(shù)定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
這個(gè)操作與前面分析的第14個(gè)操作是類(lèi)似,區(qū)別只在于前者是批量地移動(dòng)一組AppWindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象及其子 WindowState對(duì)象,而后者是只移動(dòng)一個(gè)AppWindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象及其子WindowState對(duì)象,此外,前者總是會(huì)調(diào)用WindowManagerService類(lèi)的成員函數(shù)updateFocusedWindowLocked、assignLayersLocked和performLayoutAndPlaceSurfacesLocked來(lái)更新系統(tǒng)當(dāng)前獲得焦點(diǎn)的窗口、以及重新計(jì)算每一個(gè)窗口的Z軸位置,并且對(duì)這些窗口進(jìn)行重新布局。
至此,我們就分析完成WindowManagerService服務(wù)組織系統(tǒng)中的窗口的方式了。從分析的過(guò)程中,可以得到以下結(jié)論:
1. WindowManagerService服務(wù)維護(hù)有一個(gè)AppWindowToken堆棧和一個(gè)WindowState堆棧,它們與ActivityManagerService服務(wù)維護(hù)的Actvity堆棧是有關(guān)相同的Z軸位置關(guān)系的。
2. ActivityManagerService服務(wù)中的每一個(gè)ActivityRecord對(duì)象在WindowManagerService服務(wù)中都對(duì)應(yīng)有一個(gè)AppWindowToken對(duì)象,而WindowManagerService服務(wù)中的每一個(gè)AppWindowToken對(duì)象都對(duì)應(yīng)有一組WindowState對(duì)象。
3. 在WindowState堆棧中,AppWindowToken堆棧中的第i+1個(gè)AppWindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象都位于第i個(gè)AppWindowToken對(duì)象所對(duì)應(yīng)的WindowState對(duì)象的上面。
4. 一個(gè)WindowState對(duì)象可以附加在另外一個(gè)WindowState對(duì)象上面,此外,一個(gè)WindowState對(duì)象還可以有子WindowState對(duì)象,它們都是與同一個(gè)AppWindowToken對(duì)象或者WindowToken對(duì)象所對(duì)應(yīng)的。
5. WindowManagerService服務(wù)有兩個(gè)特殊的WindowToken,它們分別用來(lái)描述系統(tǒng)中的輸入法窗口令牌和壁紙窗口令牌,其中,輸入法窗口位于需要輸入法的窗口的上面,而壁紙窗口位于需要壁紙的窗口的下面。
最后,我們可以將WindowManagerService服務(wù)中的AppWindowToken理解成一個(gè)Activity組件令牌,而將它所對(duì)應(yīng)的WindowState對(duì)象理解成一個(gè)Activity窗口。有了這些概念之后,就為學(xué)習(xí)WindowManagerService服務(wù)的各種實(shí)現(xiàn)打下堅(jiān)實(shí)的基礎(chǔ)。在接下來(lái)的兩篇文章中,我們就會(huì)在本文的基礎(chǔ)上,繼續(xù)分析WindowManagerService服務(wù)是如何管理系統(tǒng)中的輸入法窗口和壁紙窗口的,敬請(qǐng)關(guān)注!
相關(guān)文章
android之HttpPost&HttpGet使用方法介紹
下文直接講用法,先知道怎么用,再知道怎么回事,具體如下,感興趣的朋友可以參考下哈2013-06-06BottomNavigationView?ViewPager2?Fragment底部菜單導(dǎo)航欄
這篇文章主要為大家介紹了BottomNavigationView?ViewPager2?Fragment底部菜單導(dǎo)航欄實(shí)現(xiàn)效果詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11WorkManager解決應(yīng)用退出后繼續(xù)運(yùn)行后臺(tái)任務(wù)
這篇文章主要為大家介紹了WorkManager解決應(yīng)用退出后繼續(xù)運(yùn)行后臺(tái)任務(wù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07Android自定義View實(shí)現(xiàn)選座功能
這篇文章主要介紹了Android自定義View實(shí)現(xiàn)選座功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09Android用于校驗(yàn)集合參數(shù)的小封裝示例
本篇文章主要介紹了Android-用于校驗(yàn)集合參數(shù)的小封裝示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10Android訪(fǎng)問(wèn)assets本地json文件的方法
這篇文章主要介紹了Android訪(fǎng)問(wèn)assets本地json文件的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10Flutter?Android多窗口方案落地實(shí)戰(zhàn)
這篇文章主要為大家介紹了Flutter?Android多窗口方案落地實(shí)戰(zhàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02Android編程開(kāi)發(fā)之打開(kāi)文件的Intent及使用方法
這篇文章主要介紹了Android編程開(kāi)發(fā)之打開(kāi)文件的Intent及使用方法,已實(shí)例形式分析了Android打開(kāi)文件Intent的相關(guān)布局及功能實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10Ubantu16.04進(jìn)行Android 8.0源碼編譯的流程
這篇文章主要介紹了Ubantu16.04進(jìn)行Android 8.0源碼編譯的相關(guān)資料,需要的朋友可以參考下2018-02-02