我們知道,在Android系統中,Activity是以堆棧的形式組織在ActivityManagerService服務中的。與Activity類似,Android系統中的窗口也是以堆棧的形式組織在WindowManagerService服務中的,其中,Z軸位置較低的窗口位于Z軸位置較高的窗口的下面。在本文中,我們就詳細分析WindowManagerService服務是如何以堆棧的形式來組織窗口的。
從前面Android應用程序啟動過程源代碼分析一文可以知道,應用程序進程中的每一個Activity組件在Activity管理服務ActivityManagerService中都對應有一個ActivityRecord對象。從前面Android應用程序窗口(Activity)與WindowManagerService服務的連接過程分析一文又可以知道,Activity管理服務ActivityManagerService中每一個ActivityRecord對象在Window管理服務WindowManagerService中都對應有一個AppWindowToken對象。
此外,在輸入法管理服務InputMethodManagerService中,每一個輸入法窗口都對應有一個Binder對象,這個Binder對象在Window管理服務WindowManagerService又對應有一個WindowToken對象。
與輸入法窗口類似,在壁紙管理服務WallpaperManagerService中,每一個壁紙窗口都對應有一個Binder對象,這個Binder對象在Window管理服務WindowManagerService也對應有一個WindowToken對象。
在Window管理服務WindowManagerService中,無論是AppWindowToken對象,還是WindowToken對象,它們都是用來描述一組有著相同令牌的窗口的,每一個窗口都是通過一個WindowState對象來描述的。例如,一個Activity組件窗口可能有一個啟動窗口(Starting Window),還有若干個子窗口,那么這些窗口就會組成一組,并且都是以Activity組件在Window管理服務WindowManagerService中所對應的AppWindowToken對象為令牌的。從抽象的角度來看,就是在Window管理服務WindowManagerService中,每一個令牌(AppWindowToken或者WindowToken)都是用來描述一組窗口(WindowState)的,并且每一個窗口的子窗口也是與它同屬于一個組,即都有著相同的令牌。
上述的窗口組織方式如圖1所示:
圖1 窗口在WindowManagerService服務中的組織方式
其中,Activity Stack是在ActivityManagerService服務中創建的,Token List和Window Stack是在WindowManagerService中創建的,而Binder for IM和Binder for WP分別是在InputMethodManagerService服務和WallpaperManagerService服務中創建的,用來描述一個輸入法窗口和一個壁紙窗口。
圖1中的對象的對應關系如下所示:
1. ActivityRecord-J對應于AppWindowToken-J,后者描述的一組窗口是{WindowState-A, WindowState-B, WindowState-B-1},其中, WindowState-B-1是WindowState-B的子窗口。
2. ActivityRecord-K對應于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對應于AppWindowToken-N,后者描述的一組窗口是{WindowState-E},其中, WindowState-E是系統當前激活的Activity窗口。
4. Binder for IM對應于WindowToken-I,后者描述的一組窗口是{WindowState-I},其中, WindowState-I是WindowState-E的輸入法窗口。
5. Binder for WP對應于WindowToken-W,后者描述的一組窗口是{WindowState-W},其中, WindowState-W是WindowState-E的壁紙窗口。
從圖1還可以知道,Window Stack中的WindowState是按照它們所描述的窗口的Z軸位置從低到高排列的。
以上就是WindowManagerService服務組織系統中的窗口的抽象模型,接下來我們將分析AppWindowToken、WindowToken和WindowState的一些增加、移動和刪除等操作,以便可以對這個抽象模型有一個更深刻的認識。
1. 增加AppWindowToken
從前面Android應用程序窗口(Activity)與WindowManagerService服務的連接過程分析一文可以知道,一個Activity組件在啟動的過程中,ActivityManagerService服務會調用調用WindowManagerService類的成員函數addAppToken來為它增加一個AppWindowToken,如下所示:
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接口的情況下,可以迅速指出是否存在一個對應的WindowToken對象,而后者可以迅速遍歷WindowManagerService服務中的WindowToken對象。
成員變量mAppTokens指向的也是一個ArrayList,不過它里面保存的是一系列AppWindowToken對象,每一個AppWindowToken對象都是用來描述一個Activity組件窗口的,而這些AppWindowToken對象是以它們描述的窗口的Z軸坐標由小到大保存在這個ArrayList中的,這樣我們就可以通過這個ArrayList來從上到下或者從下到上地遍歷系統中的所有Activity組件窗口。由于這些AppWindowToken對象所描述的Activity組件窗口也是一個窗口,并且AppWindowToken類是從WindowToken繼承下來的,因此,這些AppWindowToken對象還會同時被保存在成員變量mTokenMap所指向的一個HashMap和成員變量mTokenList所指向的一個ArrayList中。
理解了WindowManagerService類的這三個成員變量的含義之后,它的成員函數addAppToken的實現就好理解了,其中,參數token指向的便是用來描述正在啟動的Activity組件所對應的一個ActivityRecord對象,而參數addPos用來描述該Activity組件在堆棧中的位置,這個位置同時也是接下來要創建的AppWindowToken對象在WindowManagerService類的mTokenList所描述的一個ArrayList中的位置。
WindowManagerService類的成員函數addAppToken首先調用另外一個成員函數findAppWindowToken來在成員變量mTokenMap所描述的一個HashMap檢查是否已經存在一個AppWindowToken。如果已經存在的話,那么WindowManagerService類的成員函數addAppToken就什么也不做就返回了,否則的話,就會使用參數token來創建一個AppWindowToken對象,并且會將該AppWindowToken對象分別保存在WindowManagerService類的成員變量mTokenMap、mTokenList和mAppTokens中。
2. 刪除AppWindowToken
刪除AppWindowToken是通過調用WindowManagerService類的成員函數removeAppTokensLocked來實現的,如下所示:
WindowManagerService類的成員函數removeAppTokensLocked可以同時刪除一組AppWindowToken對象。
參數tokens所描述的是一個IBinder接口列表,與這些IBinder接口所對應的AppWindowToken對象就是接下來要刪除的。WindowManagerService類的成員函數removeAppTokensLocked通過一個for循環來依次調用另外一個成員函數findAppWindowToken,以便可以找到保存在列表tokens中的每一個IBinder接口所對應的AppWindowToken對象,然后將該AppWindowToken對象從WindowManagerService類的成員變量mAppTokens所描述的一個ArrayList中刪除。
注意,WindowManagerService類的成員函數removeAppTokensLocked是在內部使用的,它只是把一個AppWindowToken對象從成員變量mAppTokens中刪除,而沒有從另外兩個成員變量mTokenMap和mTokenList中刪除。
3. 移動AppWindowToken至指定位置
移動AppWindowToken至指定位置是通過調用WindowManagerService類的成員函數moveAppToken來實現的,如下所示:
參數token描述的是要移動的AppWindowToken對象所對應的一個IBinder接口,而參數index描述的是該AppWindowToken對象要移動到的位置。注意,移動一個AppWindowToken對象到指定的位置是需要android.Manifest.permission.MANAGE_APP_TOKENS權限的。
WindowManagerService類的成員函數moveAppToken首先找到與參數token所對應的AppWindowToken對象,并且將該AppWindowToken對象從WindowManagerService類的成員變量mAppTokens所描述的一個ArrayList中移除,這樣做的目的是為了接下來可以將該AppWindowToken對象移動至該ArrayList中的指定位置上,即參數index所描述的位置上。
注意,上述操作只是將參數token所對應的AppWindowToken對象移動到了WindowManagerService類的成員變量mAppTokens所描述的一個ArrayList的指定位置上,接下來還需要同時將與該AppWindowToken對象所對應的WindowState對象移動至WindowManagerService服務內部的一個WindowState堆棧合適位置上去。
移動對應的WindowState對象的操作同樣也是分兩步執行的:第一步先調用WindowManagerService類的成員函數tmpRemoveAppWindowsLocked來將這些WindowState對象從原來的WindowState堆棧位置移除;第二步再調用WindowManagerService類的成員函數reAddAppWindowsLocked來將這些WindowState對象插入到WindowState堆棧的合適位置去。
對應的WindowState對象被移動到的合適位置是通過調用WindowManagerService類的成員函數findWindowOffsetLocked來獲得的,它的實現如下所示:
參數tokenPos描述的是一個AppWindowToken對象在WindowManagerService類的成員變量mAppTokens所描述的一個ArrayList的位置,WindowManagerService類的成員函數findWindowOffsetLocked的目標就要找到與該AppWindowToken對象所對應的WindowState對象在WindowManagerService服務內部的一個WindowState堆棧的起始偏移位置。有了這個起始偏移位置之后,我們就可以將對應的所有WindowState對象有序地插入到該WindowState堆棧中去。WindowManagerService服務內部的WindowState堆棧是通過WindowManagerService類的成員變量mWindows來描述的。接下來我們就分兩種情況來分析這個起始偏移位置的計算過程。
第一種情況是參數tokenPos的值大于WindowManagerService類的成員變量mAppTokens所描述的一個ArrayList的大小。這是一種異常情況,一般來說,參數tokenPos是指向mAppTokens列表的某一個位置的,不過這時候意味著它所描述的AppWindowToken對象的Z軸位置要大于mAppTokens列表的最上面的一個AppWindowToken對象的Z軸位置的。這也就是說,與參數tokenPos所描述的AppWindowToken對象所對應的WindowState對象的要位于與mAppTokens列表的最上面的一個AppWindowToken對象所對應的任一個WindoState對象的上面。因此,就需要找到與mAppTokens列表的最上面的一個AppWindowToken對象所對應的Z軸位置最大的一個WindoState對象在WindowState堆棧中的位置i,然后就可以知道與參數tokenPos所描述的AppWindowToken對象所對應的WindowState對象在WindowState堆棧的起始偏移位置為i+1。
如何找到mAppTokens列表的最上面的一個AppWindowToken對象所對應的Z軸位置最大的一個WindoState對象在WindowState堆棧中的位置i呢?從圖1可以可得到一個結論:WindowManagerService服務內部中的所有WindowState對象都是按照Z軸從位置從小到大排列在WindowState堆棧中的,并且在mAppTokens列表中,位于上面的一個AppWindowToken對象所對應的那些WindowState對象的Z軸位置是一定大于位于下面的一個AppWindowToken對象所對應的那些WindowState對象的Z軸位置的。因此,我們只要從WindowState堆棧的頂端開始往下遍歷,找到這樣的一個WindowState對象,它是屬于一個AppWindowToken對象的,即它的成員函數getAppToken的返回值不等于null,那么它在WindowState堆棧中的位置就是我們要找到的位置i。有了這個位置i之后,將它的值加上1,就可以得到參數t所描述的AppWindowToken對象所對應的WindowState對象在WindowState堆棧的起始偏移位置了。
第二種情況是參數tokenPos的值小于WindowManagerService類的成員變量mAppTokens所描述的一個ArrayList的大小。根據前面得到的推論,我們只要在mAppTokens列表中找到一個AppWindowToken對象,它滿足以下三個條件:
A. 它在mAppTokens列表中的位置小于tokenPos;
B. 它在WindowState堆棧中對應有WindowState對象;
C. 它不是將要置于WindowState堆棧的底部。
如果一個AppWindowToken對象在WindowState堆棧中對應有WindowState對象,那么這些WindowState對象也會同時按照Z軸從小到大的順序保存它的成員變量windows所描述的一個ArrayList中,這意味著如果一個AppWindowToken對象滿足條件B,那么它的成員變量windows所描述的一個ArrayList的大小就大于0。
如果一個AppWindowToken對象不是將要置于WindowState堆棧的底部,那么它的成員變量sendingToBottom的值就不等于true,這也意味這個AppWindowToken對象滿足條件C。
如果能找到滿足上述條件的一個AppWindowToken對象wtoken,那么我們只要找到與它所對應的Z軸位置最大的WindowState對象在WindowManagerService服務內部的WindowState堆棧中的位置i,那么將它的值加1,就可以得到與參數tokenPos所描述的AppWindowToken對象所對應的WindowState對象在WindowState堆棧的起始偏移位置了。
那么如何找到與這個AppWindowToken對象wtoken對應的Z軸位置最大的WindowState對象在WindowManagerService服務內部的WindowState堆棧中的位置i呢?從前面的圖1可以知道,一個AppWindowToken對象所對應的WindowState對象可以劃分為兩種類型:第一種類型是父窗口類型的;第二種是子窗口類型的。如果一個WindowState對象所描述的窗口是父窗口,那么它的子窗口就保存在它的成員變量mChildWindows所描述的一個ArrayList中,并且這些子窗口是按照Z軸位置從小到大的順序排列的,同時,該WindowState對象也會保存在與它所對應的一個AppWindowToken對象的成員變量windows所描述的一個ArrayList中。
有了上述結論,并且假設存在一個能夠滿足上述三個條件的AppWindowToken對象wtoken,那么就可以從上到下遍歷保存在它的成員變量windows所描述的一個ArrayList中的每一個WindowState對象win:
I. 如果WindowState對象win所描述的一個窗口具有子窗口,那么就繼續從上到下遍歷這些子窗口,即從上到下遍歷WindowState對象win的成員變量mChildWindows所描述的一個ArrayList。如果能找到一個WindowState對象cwin,它的成員變量mSubLayer的值大于等于0,那么該WindowState對象cwin在WindowManagerService服務內部的WindowState堆棧中的位置就是我們要得到的位置i。注意,如果WindowState對象cwin的成員變量mSubLayer的值小于0,那么它雖然是一個子窗口,但是它卻是位于父窗口的后面的,即它的Z軸位置是小于父窗口的Z軸位置的。
II. 如果WindowState對象win所描述的一個窗口不具有子窗口,即它的成員變量mChildWindows所描述的一個ArrayList的大小等于0,那么它在WindowManagerService服務內部的WindowState堆棧中的位置就是我們要得到的位置i。
得到了位置i之后,將它的值加1,那么就可以得到與參數tokenPos所描述的AppWindowToken對象所對應的WindowState對象在WindowState堆棧的起始偏移位置了。
回到WindowManagerService類的成員函數moveAppToken中,調整好參數token所描述的AppWindowToken對象所對應的WindowState對象在WindowState堆棧中的位置之后,即調用了成員函數reAddAppWindowsLocked之后,這時候系統中的窗口的布局就會發生了變化,即系統中的窗口的Z軸位置關系發生了變化,那么接下來就需要調用成員函數updateFocusedWindowLocked來重新計算系統中的窗口的Z軸位置,并且調用成員函數performLayoutAndPlaceSurfacesLocked來重新布局系統中的窗口。
4. 移動AppWindowToken至頂端
移動AppWindowToken至頂端是通過調用WindowManagerService類的成員函數moveAppTokensToTop來實現的,如下所示:
WindowManagerService類的成員函數moveAppTokensToTop可以同時將一組AppWindowToken移至頂端,同時需要調用者具有android.Manifest.permission.MANAGE_APP_TOKENS權限。
參數tokens所描述的是一個IBinder接口列表,與這些IBinder接口所對應的AppWindowToken對象就是接下來要移至頂端的。在將保存在參數tokens中的IBinder接口所對應的AppWindowToken對象移至頂端之前,WindowManagerService類的成員函數首先會調用前面所描述的成員函數removeAppTokensLocked來刪除這些AppWindowToken對象,然后再依次將它們添加到WindowManagerService類的成員變量mAppTokens所描述的一個ArrayList的末尾去。
注意,WindowManagerService類的成員變量mNextAppTransition用來描述系統當前是否正在切換Activity窗口。如果是的話,那么它的值就不等于WindowManagerPolicy.TRANSIT_UNSET,這時候就需要:
A. 將所有要移至頂端的AppWindowToken對象都保存在WindowManagerService類的另外一個成員變量mToTopApps所描述的一個ArrayList中去,并且將這些AppWindowToken對象的成員變量sendingToTop的值設置為true。
B. 將所有要移至頂端的AppWindowToken對象所對應WindowState對象都移至WindowManagerService服務內部的一個WindowState堆棧的頂端去,這是通過調用另外一個成員函數moveAppWindowsLocked來實現的。
執行完成上述兩個操作之后,與要移至頂端的AppWindowToken對象所對應的窗口就會位于窗口堆棧的最上面了。
5. 移動AppWindowToken至底端
移動AppWindowToken至頂端是通過調用WindowManagerService類的成員函數moveAppTokensToBottom來實現的,如下所示:
WindowManagerService類的成員函數moveAppTokensToBottom可以同時將一組AppWindowToken移至底端。將一組AppWindowToken移至底端與將一組AppWindowToken移至頂端的實現是類似的,只不過是移動的方向相反而已。因此,WindowManagerService類的成員函數moveAppTokensToBottom的實現可以參考前面所分析的成員函數moveAppTokensToTop的實現,這里不再詳述。
6. 增加WindowToken
從圖1可以知道,如果一個WindowState對象不是與一個AppWindowToken對象對應的,那么它就必須要與一個WindowToken對象對應。例如,用來描述輸入法窗口和壁紙窗口的WindowState對象對應的就是WindowToken對象,而不是AppWindowToken對象,因為它們不是Activity類型的窗口。
輸入法窗口和壁紙窗口分別是由輸入法管理服務InputMethodManagerService和壁紙管理服務WallpaperManagerService調用WindowManagerService類的成員函數addWindowToken來增加對應的WindowToken對象的,如下所示:
調用WindowManagerService類的成員函數addWindowToken需要具有android.Manifest.permission.MANAGE_APP_TOKENS權限。
對于輸入法窗口和壁紙窗口來說,參數token指向的是與它們所關聯的一個Binder對象的IBinder接口,而參數type描述的是要在WindowManagerService服務內部增加WindowToken對象的窗口的類型。
WindowManagerService類的成員函數addWindowToken首先檢查在成員變量mTokenMap所描述的一個HashMap檢查是否已經存在一個WindowToken對象與參數token對應。如果已經存在的話,那么WindowManagerService類的成員函數addWindowToken就什么也不做就返回了,否則的話,就會使用參數token來創建一個WindowToken對象,并且會將該WindowToken對象分別保存在WindowManagerService類的成員變量mTokenMap和mTokenList中。
這里有兩個地方需要注意:
A. 由于這里增加的是WindowToken對象,而不是AppWindowToken對象,因此,與增加AppWindowToken不同,這里不需要將新創建的WindowToken對象保存在WindowManagerService類的成員變量mAppTokens中。
B. 如果參數type的值等于TYPE_WALLPAPER,那么就意味著新創建的WindowToken對象是用來描述壁紙窗口的,這時候還需要將新創建的WindowToken對象保存在WindowManagerService類的成員變量mWallpaperTokens所描述的一個ArrayList中,以方便管理壁紙窗口。
對于非輸入法窗口、非壁紙窗口以及非Activity窗口來說,它們所對應的WindowToken對象是在它們增加到WindowManagerService服務的時候創建的。從前面Android應用程序窗口(Activity)與WindowManagerService服務的連接過程分析一文可以知道,增加一個窗口WindowManagerService服務最終是通過調用WindowManagerService類的成員函數addWindow來實現的,接下來我們就主要分析與創建WindowToken相關的邏輯,如下所示:
如果參數attrs所描述的一個WindowManager.LayoutParams對象的成員變量token所指向的一個IBinder接口在WindowManagerService類的成員變量mTokenMap所描述的一個HashMap中沒有一個對應的WindowToken對象,并且該WindowManager.LayoutParams對象的成員變量type的值不等于TYPE_INPUT_METHOD、TYPE_WALLPAPER,以及不在FIRST_APPLICATION_WINDOW和LAST_APPLICATION_WINDOW,那么就意味著這時候要增加的窗口就既不是輸入法窗口,也不是壁紙窗口和Activity窗口,因此,就需要以參數attrs所描述的一個WindowManager.LayoutParams對象的成員變量token所指向的一個IBinder接口為參數來創建一個WindowToken對象,并且將該WindowToken對象保存在WindowManagerService類的成員變量mTokenMap和mTokenList中。
7. 刪除WindowToken
刪除WindowToken是通過調用WindowManagerService類的成員函數removeWindowToken來實現的,如下所示:
調用WindowManagerService類的成員函數removeWindowToken需要具有android.Manifest.permission.MANAGE_APP_TOKENS權限。
WindowManagerService類的成員函數removeWindowToken首先找到與參數token所描述的Binder接口所對應的WindowToken對象,接著再將該WindowToken對象從WindowManagerService類的成員變量mTokenMap和mTokenList中刪除。
刪除了一個WindowToken對象之后,如果該WindowToken對象不是處于不可見的狀態,即它的成員變量hidden的值不等于false,那么就意味著它所描述窗口口也有可能是可見的,那么WindowManagerService類的成員函數removeWindowToken就需要作以下兩個檢查:
A. 如果該WindowToken對象所描述的窗口的其中一個處于動畫顯示過程,即用來描述該窗口的一個WindowState對象的成員函數isAnimating的返回值等于true,那么就需要該WindowToken對象的狀態設置為正在退出狀態,即將它保存在WindowManagerService類的成員變量mExitingTokens所描述的一個ArrayList中。
B. 如果該WindowToken對象所描述的窗口是可見的,即用來描述該窗口的一個WindowState對象的成員函數isVisibleNow的返回值等于true,那么就需要調用WindowManagerService類的成員函數applyAnimationLocked來給它應用一個退出動畫,該退出動畫是通過調用WindowManagerService類的成員函數performLayoutAndPlaceSurfacesLocked來實現的。當一個窗口退出了之后,系統當前獲得焦點的窗口可能會發生變化,這時候就需要調用WindowManagerService類的成員函數updateFocusedWindowLocked來重新調整系統當前獲得焦點的窗口。
注意,如果正在刪除的WindowToken對象是用來描述壁紙窗口的,那么還需要將該WindowToken對象從WindowManagerService類的成員變量mWallpaperTokens所描述的一個ArrayList中刪除。
8. 增加WindowState
從前面Android應用程序窗口(Activity)與WindowManagerService服務的連接過程分析一文可以知道,增加一個窗口WindowManagerService服務最終是通過調用WindowManagerService類的成員函數addWindow來實現的,如下所示:
WindowManagerService類有兩個成員變量mWindowMap和mWindows是用來保存系統中的WindowState對象。其中,成員變量mWindowMap指向的是一個HashMap,它的關鍵字是一個IBinder接口,一般這個IBinder接口指向的是一個Binder代理對象,引用了運行在應用程序進程這一側的一個類型為W的Binder本地對象,用來描述一個窗口;成員變量mWindows指向的是一個ArrayList,保存在它里面的WindowState對象是按照其Z軸位置從小到大的順序排列的。成員變量mWindowMap和mWindows的區別在于,前者給在定一個IBinder接口的情況下,可以快速找到與對應的WindowState對象,而后者用來從上到下或者下到上遍歷系統的WindowState對象。由于系統中的WindowState對象是按照其Z軸位置從小到大的順序排列在成員變量mWindows中的,因此,成員變量mWindows所指向的ArrayList就是我們在前面圖1中所說的Window Stack。
理解了WindowManagerService類有兩個成員變量mWindowMap和mWindows的作用之后,WindowManagerService類的成員函數addWindow增加一個WindowState對象的過程就容易理解了。
參數client是一個Binder代理對象,引用了運行在應用程序進程這一側的一個類型為W的Binder本地對象,用來描述要增加到WindowManagerService服務中的一個窗口。WindowManagerService類的成員函數addWindow首先創建一個WindowState對象win,接著再以參數client所描述的一個Binder代理對象的IBinder接口為關鍵字,將WindowState對象win保存在WindowManagerService類的成員變量mWindowMap中,最后還會根據要增加到WindowManagerService服務中的窗口的類型來調用不同的成員函數將WindowState對象win增加到WindowManagerService類的成員變量mWindows中:
A. 如果要增加的是輸入法窗口,即參數attrs所描述的一個WindowManager.LayoutParams對象的成員變量type的值等于TYPE_INPUT_METHOD,那么就會調用成員函數addInputMethodWindowToListLocked來將WindowState對象win增加到WindowManagerService類的成員變量mWindows中去,并且會將WindowState對象win保存在WindowManagerService類的成員變量mInputMethodWindow中。
B. 如果要增加的是輸入法對話框,即參數attrs所描述的一個WindowManager.LayoutParams對象的成員變量type的值等于TYPE_INPUT_METHOD_DIALOG,那么就會調用成員函數addWindowToListInOrderLocked來將WindowState對象win增加到WindowManagerService類的成員變量mWindows中去,并且會將WindowState對象win保存在WindowManagerService類的成員變量mInputMethodDialogs中,以及調用成員函數adjustInputMethodDialogsLocked來調整剛才所添加的輸入法窗口在窗口堆棧中的位置,使得它位于系統當前需要輸入法窗口的窗口的上面。
C. 如果要增加的是壁紙窗口,即參數attrs所描述的一個WindowManager.LayoutParams對象的成員變量type的值等于TYPE_WALLPAPER,那么就會調用成員函數addWindowToListInOrderLocked來將WindowState對象win增加到WindowManagerService類的成員變量mWindows中去,并且會調用成員函數adjustWallpaperWindowsLocked來調整剛才所添加的壁紙窗口在窗口堆棧中的位置,使得它位于系統當前需要壁紙窗口的窗口的下面。
D . 如果要增加的既不是輸入法窗口,也不是輸入法對話框和壁紙窗口,那么就只會調用成員函數addWindowToListInOrderLocked來將WindowState對象win增加到WindowManagerService類的成員變量mWindows中去,但是如果要增加的窗口需要顯示壁紙,即參數attrs所描述的一個WindowManager.LayoutParams對象的成員變量flags的FLAG_SHOW_WALLPAPER位等于1,那么還會繼續調用成員函數adjustWallpaperWindowsLocked來調整系統中的壁紙窗口在窗口堆棧中的位置,使得它位于剛才所添加的窗口的下面。
在后面的兩篇文章中,我們再詳細分析WindowManagerService類的成員函數addInputMethodWindowToListLocked、adjustInputMethodDialogsLocked和adjustWallpaperWindowsLocked的實現,其中,前兩者是與輸入法窗口相關的,而后者是與壁紙窗口相關的。本文主要關注WindowManagerService類的成員函數addWindowToListInOrderLocked的實現,它會將一個指定的WindowState對象增加到窗口堆棧中的合適位置上去。
9. 增加WindowState到窗口堆棧
從前面的分析可以知道,將一個WindowState對象增加到WindowManagerService服務內部中的窗口堆棧,即WindowManagerService類的成員變量mWindows,是通過調用WindowManagerService類的成員函數addWindowToListInOrderLocked來實現的。
WindowManagerService類的成員函數addWindowToListInOrderLocked的實現比較復雜,我們先列出它的框架,然后再詳細分析它的實現,如下所示:
我們首先分析一下WindowManagerService類的成員函數addWindowToListInOrderLocked的幾個本地變量的含義:
A. token。本地變量token指向的是參數win所描述的一個WindowState對象的成員變量mToken所指向一個WindowToken對象,這個WindowToken對象用來描述WindowState對象win所對應的窗口令牌。
B. localmWindows。本地變量localmWindows指向的是WindowManagerService類的成員變量mWindows所描述的一個ArrayList,即一個窗口堆棧,WindowManagerService類的成員函數addWindowToListInOrderLocked的目標就是要將參數win所描述的一個WindowState對象增加到該窗口堆棧的合適位置上去。
C. attached。本地變量attached指向的是參數win所描述的一個WindowState對象的成員變量mAttachedWindow 所指向的一個WindowState對象,如果它的值不等于null,那么就意味參數win所描述的窗口要附加在本地變量attached所描述的窗口上。
D. tokenWindowsPos。本地變量tokenWindowsPos用來描述與窗口令牌token所對應的窗口的數量。
E. token.appWindowToken。從前面Android應用程序窗口(Activity)與WindowManagerService服務的連接過程分析一文可以知道,如果一個WindowToken對象的成員變量appWindowToken的值不等于null,那么就意味著該WindowToken對象的實際類型為是AppWindowToken,即它所描述的是一個Activity窗口令牌,這種類型的令牌的特點是在ActivityManagerService服務的Activity組件堆棧中對應有一個ActivityRecord對象,如圖1所示。
F. index。本地變量index的值等于tokenWindowsPos-1,如果它的值大于等于0,那么就意味著窗口令牌tokent已經存在其它窗口,否則的話,就意味著窗口令牌tokent尚未存在任何窗口。
從這些本地變量的含義,我們就可以分情況來將參數win所描述的一個WindowState對象增加到WindowManagerService服務內部的窗口堆棧的合適位置上去:
CASE 1:要增加的窗口win沒有附加在其它窗口上
----CASE 1.1:要增加的窗口win是一個Activity窗口
----CASE 1.1.1:用來要增加的窗口win的令牌token已存在其它窗口。這時候意味著窗口win需要保存在其它已經存在的窗口的附近,因此,我們只要找到這些已經存在的窗口在窗口堆棧中的位置,那么再根據其它屬性,就可以將窗口win保存在已經存在的窗口的上面或者下面。
----CASE 1.1.2:用來要增加的窗口win的令牌token尚未存在任何窗口。雖然這時候窗口win在窗口堆棧中沒有位置可以參考,但是它畢竟是一個Activity窗口,我們可以通過與它所對應的AppWindowToken對象在App Token List(即WindowManagerService類的成員變量mAppTokens所描述的一個ArrayList)中的位置來獲得它窗口堆棧中的位置。回憶我們在前面第3節分析移動AppWindowToken至指定位置的操作時得到的結論:WindowManagerService服務內部中的所有WindowState對象都是按照Z軸從位置從小到大排列在WindowState堆棧中的,并且在mAppTokens列表中,位于上面的一個AppWindowToken對象所對應的那些WindowState對象的Z軸位置是一定大于位于下面的一個AppWindowToken對象所對應的那些WindowState對象的Z軸位置的。因此,我們只要找到用來描述窗口win的一個AppWindowToken對象(token.appWindowToken)的上一個或者下一個AppWindowToken對象所對應的窗口在窗口堆棧中的位置,那么就可以這個位置為參考,得到窗口win在窗口堆棧中的位置。
----CASE 1.2:要增加的窗口win不是一個Activity窗口。這時候既然要增加的窗口也沒有附加在其它窗口上,那么就意味著要增加的窗口win在窗口堆棧中沒有位置可以參考,因此,我們就需要根據它的Z軸位置來決定它在窗口堆棧的位置。
CASE 2:要增加的窗口win附加在窗口attached上。這時候就意味著要增加的窗口win要保存在窗口attached的上面,即窗口在窗口堆棧的位置要以窗口attached在窗口堆棧的位置為參考。
從上面的分析就可以知道,CASE 1.1.1、CASE 1.1.2和CASE 2都有一個共同特點,即要增加的窗口win在窗口堆棧的位置有一個參考值,而在CASE 1.2中,要增加的窗口win在窗口堆棧的位置沒有參考值,需要通過其Z軸位置來確定。
在分析上述四種情況之前, 我們還需要再說明一下WindowManagerService類的成員函數addWindowToListInOrderLocked的參數addToToken的含義。參數addToToken是一個布爾變量,如果它的值等于true,那么就說明需要將參數win所描述的一個WindowState對象添加用來描述它的窗口令牌token的成員變量windows所描述的一個ArrayList中去。注意,窗口令牌token的成員變量windows所描述的一個ArrayList里面所保存的WindowState對象是按照Z軸位置從小到大的順序來排列的,因此,在將WindowState對象win保存到這個ArrayList之前,首先要按照它的Z軸位置計算得到它在這個ArrayList中的位置tokenWindowsPos。另一方面,在參數addToToken的值等于true,并且參數win所描述的是一個Activity窗口,即它的成員變量mAppToken不等于null的情況下,還需要將參數win所描述的一個WindowState對象保存在用來描述它的窗口令牌,即一個AppWindowToken對象成員變量allAppWindows所描述的一個ArrayList中去,以便可以知道一個AppWindowToken對象對應的Activity窗口都有哪些。
接下來,我們就分別分析這四種情況是如何將窗口win增加窗口堆棧中去的。
CASE 1.1.1對應的代碼為:
A. 參數win描述的窗口的類型為TYPE_BASE_APPLICATION。在一個令牌對應的所有窗口中,類型為TYPE_BASE_APPLICATION的窗口位于其它類型的窗口的下面。因此,這段代碼就會調用WindowManagerService類的成員函數placeWindowBefore來將參數win所描述的一個WindowState對象保存窗口堆棧中,并且它是位于令牌token的窗口列表的第0個位置的WindowState對象的下面。這時候變量tokenWindowsPos的值會被設置為0,表示參數win所描述的一個WindowState對象要保存窗口令牌token的窗口列表的第0個位置上。
B. 參數win描述的一個WindowState對象的成員變量mAppToken的值不等于null,這意味著參數win描述的是一個Activity窗口,這時候如果窗口令牌atoken(與token描述的是同一個窗口令牌)的窗口列表的第index個位置(即最上面的一個位置) 的WindowState對象描述的是一個Activity啟動窗口,即與窗口令牌atoken的成員變量startingWindow描述的是同一個窗口,那么就說明窗口令牌atoken的窗口列表的第index個位置的WindowState對象描述的是窗口win的啟動窗口。由于一個窗口的啟動窗口總是位于它的上面,因此,這段代碼就會調用WindowManagerService類的成員函數placeWindowBefore來將參數win所描述的一個WindowState對象保存窗口堆棧中,并且它是位于令牌atoken的窗口列表的第index個位置的WindowState對象的下面。這時候變量tokenWindowsPos的值減少1,即相當于是等于index,表示參數win所描述的一個WindowState對象要插入在窗口令牌token的窗口列表的第index個位置上。
C. 參數win所描述的窗口的類型既不是TYPE_BASE_APPLICATION,而且它也沒有啟動窗口,那么這時候就需要將它保存在窗口令牌token的窗口列表的最上面一個窗口的上面。窗口令牌token的窗口列表的最上面一個窗口在窗口堆棧中的位置newIdx是通過調用WindowManagerService類的成員函數findIdxBaseOnAppTokens來獲得的,這時候參數win所描述的一個WindowState對象就應該保存在窗口堆棧,即變量localmWindows所描述的一個ArrayList的第newIdx+1個位置上。
CASE 1.1.2對應的代碼為:
最上面的一個for循環執行完成之后,我們假設變量pos的值不等于null,這時候它與變量i以及變量token的關系如圖2所示:
圖2 窗口win位于窗口C的下面
這時候位于令牌token上面的令牌在窗口堆棧中對應有WindowState對象。注意,這時候第i+2個令牌在窗口堆棧中不對應有WindowState對象,而第i+3個令牌在窗口堆棧中對應有C和D兩個WindowState對象,并且這兩個WindowState對象所描述的窗口都不是即將要切換到窗口堆棧的底部的。由于第i+3個令牌位于令牌token的上面,并且這兩個令牌之間的其它令牌在窗口堆棧中不對應有WindowState對象,因此,這時候參數win所描述的WindowState對象在窗口堆棧中的位置應該以第i+3個令牌所對應的Z軸位置最小的WindowState對象在窗口堆棧中的位置為參考,即以WindowState對象C在窗口堆棧中的位置為參考,而WindowState對象C也正好是變量pos所指向的WindowState對象。
接下來,上述代碼會繼續檢查WindowState對象C是否附加有SubLayer值小于0的窗口。如果有的話,那么就會將變量pos指向SubLayer值最小的那個WindowState對象,這是因為該WindowState對象是在WindowState對象C的最下面的,并且它與WindowState對象C是同屬一個令牌的。最后,上述代碼就會調用WindowManagerService類的成員函數placeWindowBefore來將參數win所描述的一個WindowState對象保存窗口堆棧中由變量pos所指向的那個WindowState對象的下面。
假設最上面的一個for循環執行完成之后,變量pos的值等于null,那么就說明位于令牌token上面的令牌在窗口堆棧中都沒有對應有WindowState對象,或者說它們所對應的WindowState對象都是即將要切換到窗口堆棧的底部去的,這時候就需要通過位于令牌token上面的令牌來在窗口堆棧中找到一個參考位置來保存參數win所描述的WindowState對象,這是通過中間的while循環來實現的。
中間的while循環執行完成之后,假設變量pos的值不等于null,這時候它與變量i以及變量token的關系如圖3所示:
圖3 窗口win位于窗口D的上面
這時候位于令牌token上面的令牌在窗口堆棧中沒有對應有WindowState對象。注意,這時候第i-1個令牌在窗口堆棧中不對應有WindowState對象,而第i-2個令牌在窗口堆棧中對應有C和D兩個WindowState對象。由于第i-2個令牌位于令牌token的下面,并且這兩個令牌之間的其它令牌在窗口堆棧中不對應有WindowState對象,因此,這時候參數win所描述的WindowState對象在窗口堆棧中的位置應該以第i-2個令牌所對應的Z軸位置最大的WindowState對象在窗口堆棧中的位置為參考,即以WindowState對象D在窗口堆棧中的位置為參考,而WindowState對象D也正好是變量pos所指向的WindowState對象。
接下來,上述代碼會繼續檢查WindowState對象D是否附加有SubLayer值大于等于0的窗口。如果有的話,那么就會將變量pos指向SubLayer值最大的那個WindowState對象,這是因為該WindowState對象是在WindowState對象D的最上面的,并且它與WindowState對象D是同屬一個令牌的。最后,上述代碼就會調用WindowManagerService類的成員函數placeWindowAfter來將參數win所描述的一個WindowState對象保存窗口堆棧中由變量pos所指向的那個WindowState對象的上面。
假設中間的while循環執行完成之后,變量pos的值等于null,這時候就說明在窗口堆棧中實在是找不到參考位置來保存參數win所描述的WindowState對象了,因此,就只能通過參數win所描述的WindowState對象的Z軸位置,即它的成員變量mBaseLayer的值來在窗口堆棧中找到一個合適的位置了,如最下面的for循環所示。由于窗口堆棧中的WindowState對象是按照它們的Z軸位置由小到大的順序來排列的,因此,最下面的for循環只要從下到上找到一個Z軸位置比參數win所描述的WindowState對象的Z軸位置大的一個WindowState對象在窗口堆棧中的位置i,那么就可以將參數win所描述的WindowState對象插入在窗口堆棧的第i個位置上了。
CASE 1.2對應的代碼為:
CASE 2對應的代碼為:
第一種情況是參數win所描述的WindowState對象的成員變量mSubLayer的值小于0,并且這時候在附加在窗口attached的WindowState對象中,存在一個WindowState對象,它的成員變量mSubLayer的值大于等于參數win所描述的WindowState對象的成員變量mSubLayer的值,如圖4和圖5所示:
圖4 窗口win插入到窗口B的下面
圖5 窗口win插入在窗口attached的下面
在圖4和圖5中,WindowState對象A和B均是附加在WindowState對象attached中。
在圖4中,WindowState對象A和B的成員變量mSubLayer的值均小于0,而WindowState對象win的成員變量mSubLayer的值比WindowState對象A的大,但是比WindowState對象B的小,這時候WindowState對象win在窗口堆棧中就應該位于WindowState對象B的下面,這是通過調用WindowManagerService類的成員函數placeWindowBefore來實現的。
在圖5中,WindowState對象A和B的成員變量mSubLayer的值均大于0,由于WindowState對象win的成員變量mSubLayer的值小于0,這時候WindowState對象win在窗口堆棧中就應該位于WindowState對象attached的下面,這是通過調用WindowManagerService類的成員函數placeWindowBefore來實現的。
第二種情況是參數win所描述的WindowState對象的成員變量mSubLayer的值大于0,并且這時候在附加在窗口attached的WindowState對象中,存在一個WindowState對象,它的成員變量mSubLayer的值大于參數win所描述的WindowState對象的成員變量mSubLayer的值,如圖6所示:
圖6 窗口win插入在窗口B的下面
在圖6中,WindowState對象A和B均是附加在WindowState對象attached中。其中,WindowState對象A和B的成員變量mSubLayer的值均大于0,而WindowState對象win的成員變量mSubLayer的值比WindowState對象A的大,但是比WindowState對象B的小,這時候WindowState對象win在窗口堆棧中就應該位于WindowState對象B的下面,這是通過調用WindowManagerService類的成員函數placeWindowBefore來實現的。
第三種情況是參數win所描述的WindowState對象的成員變量mSubLayer的值小于0,但是在附加在窗口attached的WindowState對象中,找不到一個WindowState對象,它的成員變量mSubLayer的值比WindowState對象的成員變量mSubLayer的值大,如圖7所示:
圖7 窗口win插入在窗口attached的下面
在圖7中,WindowState對象A和B均是附加在WindowState對象attached中。其中,WindowState對象A和B以及win的成員變量mSubLayer的值均小于0,但是WindowState對象win的成員變量mSubLayer的值比WindowState對象A和B的都要大,這時候WindowState對象win在窗口堆棧中就應該位于WindowState對象attached的下面,這是通過調用WindowManagerService類的成員函數placeWindowBefore來實現的。
第四種情況是參數win所描述的WindowState對象的成員變量mSubLayer的值大于等于0,但是在附加在窗口attached的WindowState對象中,找不到一個WindowState對象,它的成員變量mSubLayer的值比WindowState對象的成員變量mSubLayer的值大,如圖8和圖9所示:
圖8 窗口win插入在窗口B的上面
圖9 窗口win插入在窗口attached的上面
在圖8和圖9中,WindowState對象A和B均是附加在WindowState對象attached中。
在圖8中,WindowState對象A和B的成員變量mSubLayer的值均大于0,并且WindowState對象win的成員變量mSubLayer的值比WindowState對象A和B的都要大,這時候WindowState對象win在窗口堆棧中就應該位于WindowState對象B的上面,這是通過調用WindowManagerService類的成員函數placeWindowAfter來實現的。
在圖9中,WindowState對象A和B的成員變量mSubLayer的值均小于等于0,而WindowState對象win的成員變量mSubLayer的值大于0,這時候WindowState對象win在窗口堆棧中就應該位于WindowState對象attached的上面,這是通過調用WindowManagerService類的成員函數placeWindowAfter來實現的。
注意,在這四種情況中,如果參數addToToken的值等于true,那么都需要將參數win所描述的WindowState對象增加到與它所對應的窗口令牌token的窗口列表windows中去。
10. 刪除WindowState
刪除WindowState是通過調用WindowManagerService類的成員函數tmpRemoveWindowLocked來實現的,如下所示:
WindowManagerService類的成員函數tmpRemoveWindowLocked將參數win所描述的窗口及其子窗口從WindowManagerService服務內部的窗口堆棧中刪除,即從 WindowManagerService類的成員變量mWindows所描述的一個ArrayList中刪除。
如果每一個被刪除的窗口在窗口堆棧中的位置比參數interestingPos的值小,那么WindowManagerService類的成員函數tmpRemoveWindowLocked還會將參數interestingPos的值減少1,這相當于是計算當刪除參數win所描述的窗口及其子窗口之后,原來位于窗口堆棧中第interestingPos個位置的窗口現在位于窗口堆棧的位置,這個位置最終會作為WindowManagerService類的成員函數tmpRemoveWindowLocked的返回值。
11. 在指定位置增加WindowState
在指定位置增加WindowState是通過調用WindowManagerService類的成員函數reAddWindowLocked來實現的,如下所示:
參數win描述的即為要增加的WindowState對象,而參數index描述的即為要將參數win所描述的WindowState對象及其子WindowState對象要增加到窗口堆棧中的起始位置。
由于參數win所描述的WindowState對象的子WindowState對象的成員變量mSubLayer的值可能會小于0,也可能大于0。大于0的子WindowState對象位于參數win所描述的WindowState對象的上面,而小于0的子WindowState對象位于參數win所描述的WindowState對象的下面。因此,WindowManagerService類的成員函數reAddWindowLocked先增加那些小于0的子WindowState對象,接著再增加參數win所描述的WindowState對象,最后增加那些大于0的子WindowState對象。
假設WindowManagerService類的成員函數reAddWindowLocked一共在窗口堆棧中增加了N個WindowState對象,那么它的返回值就等于index + N,這樣調用者就可以知道參數win所描述的WindowState對象及其子WindowState對象在窗口堆棧中的最高位置是多少。
基于第9、第10和第11這三操作,可以組合成很多其它的WindowState操作,如接下來的第12、第13、第14和第15個操作所示。
12. 將一個WindowState對象及其所有子WindowState對象增加到窗口堆棧中
將一個WindowState對象及其所有子WindowState對象增加到窗口堆棧中是通過調用WindowManagerService類的成員函數reAddWindowToListInOrderLocked來實現的,如下所示:
為了得到參數win所描述的WindowState對象的子WindowState對象在窗口堆棧中的起始位置,WindowManagerService類的成員函數reAddWindowToListInOrderLocked首先將參數win所描述的WindowState對象增加到窗口堆棧中,這是通過調用前面所分析的成員函數addWindowToListInOrderLocked來實現的,目的是為了獲得它在窗口堆棧的位置。有了這個位置之后,WindowManagerService類的成員函數reAddWindowToListInOrderLocked就可以調用前面所分析的成員函數reAddWindowLocked來將WindowState對象及其所有子WindowState對象增加到窗口堆棧中去了,不過在調用之前,要先將參數win所描述的WindowState對象從窗口中堆棧刪除。
13. 將一個WindowToken對象對應的所有WindowState對象及其子WindowState對象增加到窗口堆棧的指定位置上
將一個WindowToken對象對應的所有WindowState對象都增加到窗口堆棧中是通過調用WindowManagerService類的成員函數reAddAppWindowsLocked來實現的,如下所示:
與參數token所描述的WindowToken對象所對應的WindowState對象保存在它的成員變量windows所描述的一個ArrayList中。通過遍歷這個ArrayList,就可以將與參數token所描述的WindowToken對象所對應的WindowState對象及其子WindowState對象都增加到窗口堆棧的指定的起始位置上去,這是通過調用前面所分析的成員函數reAddWindowLocked來實現的。
參數index描述的便是最初指定的起始位置,每一次調用WindowManagerService類的成員函數reAddWindowLocked之后,它的值都便會被更新為下一個WindowState對象及其子WindowState對象要增加到窗口堆棧中的位置。
最后,WindowManagerService類的成員函數reAddAppWindowsLocked將與參數token所描述的WindowToken對象所對應的WindowState對象在窗口堆棧中的最高位置加1后的得到結果返回給調用者。
14. 將一個AppWindowToken對象所對應的WindowState對象及其子 WindowState對象移動到窗口堆棧的指定位置上
將一個AppWindowToken對象所對應的WindowState對象及其子 WindowState對象移動到窗口堆棧的指定位置上是通過調用WindowManagerService類的成員函數moveAppWindowsLocked來實現的,如下所示:
參數wtoken描述的是要移動其所對應的WindowState對象的一個AppWindowToken對象,而參數tokenPos描述的是該AppWindowToken對象在WindowManagerService服務內部的AppWindowToken列表中的新位置。
WindowManagerService類的成員函數moveAppWindowsLocked首先調用前面所分析的成員函數tmpRemoveAppWindowsLocked來移除所有與參數wtoken所描述的AppWindowToken對象所對應的WindowState對象,接著再調用也是前面所分析的成員函數findWindowOffsetLocked來獲得與參數wtoken所描述的AppWindowToken對象所對應的WindowState對象在窗口堆棧中的起始位置。有了這個起始位置之后,就可以也是前面所分析的成員函數reAddAppWindowsLocked來將與參數wtoken所描述的AppWindowToken對象所對應的WindowState對象及其子WindowState對象移動到窗口堆棧上去了。
最后,如果參數updateFocusAndLayout的值等于true,那么WindowManagerService類的成員函數moveAppWindowsLocked還會更新系統當前獲得焦點的窗口,以及重新計算系統中的所有窗口的Z軸位置以及重新布局系統中的所有窗口,這三個操作分別是通過調用WindowManagerService類的成員函數updateFocusedWindowLocked、assignLayersLocked和performLayoutAndPlaceSurfacesLocked來實現的。
15. 將一組AppWindowToken對象所對應的WindowState對象及其子 WindowState對象移動到窗口堆棧的指定位置上
將一組AppWindowToken對象所對應的WindowState對象及其子WindowState對象移動到窗口堆棧的指定位置上是通過調用WindowManagerService類的另外一個版本的成員函數moveAppWindowsLocked來實現的,如下所示:
這個操作與前面分析的第14個操作是類似,區別只在于前者是批量地移動一組AppWindowToken對象所對應的WindowState對象及其子 WindowState對象,而后者是只移動一個AppWindowToken對象所對應的WindowState對象及其子WindowState對象,此外,前者總是會調用WindowManagerService類的成員函數updateFocusedWindowLocked、assignLayersLocked和performLayoutAndPlaceSurfacesLocked來更新系統當前獲得焦點的窗口、以及重新計算每一個窗口的Z軸位置,并且對這些窗口進行重新布局。
至此,我們就分析完成WindowManagerService服務組織系統中的窗口的方式了。從分析的過程中,可以得到以下結論:
1. WindowManagerService服務維護有一個AppWindowToken堆棧和一個WindowState堆棧,它們與ActivityManagerService服務維護的Actvity堆棧是有關相同的Z軸位置關系的。
2. ActivityManagerService服務中的每一個ActivityRecord對象在WindowManagerService服務中都對應有一個AppWindowToken對象,而WindowManagerService服務中的每一個AppWindowToken對象都對應有一組WindowState對象。
3. 在WindowState堆棧中,AppWindowToken堆棧中的第i+1個AppWindowToken對象所對應的WindowState對象都位于第i個AppWindowToken對象所對應的WindowState對象的上面。
4. 一個WindowState對象可以附加在另外一個WindowState對象上面,此外,一個WindowState對象還可以有子WindowState對象,它們都是與同一個AppWindowToken對象或者WindowToken對象所對應的。
5. WindowManagerService服務有兩個特殊的WindowToken,它們分別用來描述系統中的輸入法窗口令牌和壁紙窗口令牌,其中,輸入法窗口位于需要輸入法的窗口的上面,而壁紙窗口位于需要壁紙的窗口的下面。
最后,我們可以將WindowManagerService服務中的AppWindowToken理解成一個Activity組件令牌,而將它所對應的WindowState對象理解成一個Activity窗口。有了這些概念之后,就為學習WindowManagerService服務的各種實現打下堅實的基礎。在接下來的兩篇文章中,我們就會在本文的基礎上,繼續分析WindowManagerService服務是如何管理系統中的輸入法窗口和壁紙窗口的,敬請關注!
新聞熱點
疑難解答
圖片精選