Android窗口管理服務WindowManagerService對窗口的組織方式分析

文章轉載至CSDN社區羅昇陽的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8498908java

咱們知道,在Android系統中,Activity是以堆棧的形式組織在ActivityManagerService服務中的。與 Activity相似,Android系統中的窗口也是以堆棧的形式組織在WindowManagerService服務中的,其中,Z軸位置較低的窗口 位於Z軸位置較高的窗口的下面。在本文中,咱們就詳細分析WindowManagerService服務是如何以堆棧的形式來組織窗口的。android

        從前面Android應用程序啓動過程源代碼分析一文能夠知道,應用程序進程中的每個Activity組件在Activity管理服務ActivityManagerService中都對應有一個ActivityRecord對象。從前面Android應用程序窗口(Activity)與WindowManagerService服務的鏈接過程分析一文又能夠知道,Activity管理服務ActivityManagerService中每個ActivityRecord對象在Window管理服務WindowManagerService中都對應有一個AppWindowToken對象。windows

        此外,在輸入法管理服務InputMethodManagerService中,每個輸入法窗口都對應有一個Binder對象,這個Binder對象在 Window管理服務WindowManagerService又對應有一個WindowToken對象。session

        與輸入法窗口相似,在壁紙管理服務WallpaperManagerService中,每個壁紙窗口都對應有一個Binder對象,這個Binder對 象在Window管理服務WindowManagerService也對應有一個WindowToken對象。app

        在Window管理服務WindowManagerService中,不管是AppWindowToken對象,仍是WindowToken對象,它們都 是用來描述一組有着相同令牌的窗口的,每個窗口都是經過一個WindowState對象來描述的。例如,一個Activity組件窗口可能有一個啓動窗 口(Starting Window),還有若干個子窗口,那麼這些窗口就會組成一組,而且都是以Activity組件在Window管理服務 WindowManagerService中所對應的AppWindowToken對象爲令牌的。從抽象的角度來看,就是在Window管理服務 WindowManagerService中,每個令牌(AppWindowToken或者WindowToken)都是用來描述一組窗口 (WindowState)的,而且每個窗口的子窗口也是與它同屬於一個組,即都有着相同的令牌。框架

        上述的窗口組織方式如圖1所示:svg

圖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,以下所示:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public class WindowManagerService extends IWindowManager.Stub    
  2.         implements Watchdog.Monitor {    
  3.     ......    
  4.     
  5.     /**  
  6.      * Mapping from a token IBinder to a WindowToken object.  
  7.      */    
  8.     final HashMap<IBinder, WindowToken> mTokenMap =    
  9.             new HashMap<IBinder, WindowToken>();    
  10.     
  11.     /**  
  12.      * The same tokens as mTokenMap, stored in a list for efficient iteration  
  13.      * over them.  
  14.      */    
  15.     final ArrayList<WindowToken> mTokenList = new ArrayList<WindowToken>();    
  16.     ......    
  17.     
  18.     /**  
  19.      * Z-ordered (bottom-most first) list of all application tokens, for  
  20.      * controlling the ordering of windows in different applications.  This  
  21.      * contains WindowToken objects.  
  22.      */    
  23.     final ArrayList<AppWindowToken> mAppTokens = new ArrayList<AppWindowToken>();    
  24.     ......    
  25.     
  26.     public void addAppToken(int addPos, IApplicationToken token,    
  27.             int groupId, int requestedOrientation, boolean fullscreen) {    
  28.         ......    
  29.     
  30.         synchronized(mWindowMap) {    
  31.             AppWindowToken wtoken = findAppWindowToken(token.asBinder());    
  32.             if (wtoken != null) {    
  33.                 ......    
  34.                 return;    
  35.             }    
  36.             wtoken = new AppWindowToken(token);    
  37.             ......    
  38.             mAppTokens.add(addPos, wtoken);    
  39.             ......    
  40.             mTokenMap.put(token.asBinder(), wtoken);    
  41.             mTokenList.add(wtoken);    
  42.     
  43.             ......   
  44.         }    
  45.     }    
  46.     
  47.     ......    
  48. }    

       這個函數定義在文件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接口的狀況下,能夠迅速指出是否存在一個對應的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來實現的,以下所示:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public class WindowManagerService extends IWindowManager.Stub    
  2.         implements Watchdog.Monitor {    
  3.     ......    
  4.     
  5.     private void removeAppTokensLocked(List<IBinder> tokens) {  
  6.         // XXX This should be done more efficiently!  
  7.         // (take advantage of the fact that both lists should be  
  8.         // ordered in the same way.)  
  9.         int N = tokens.size();  
  10.         for (int i=0; i<N; i++) {  
  11.             IBinder token = tokens.get(i);  
  12.             final AppWindowToken wtoken = findAppWindowToken(token);  
  13.             if (!mAppTokens.remove(wtoken)) {  
  14.                 ......  
  15.                 i--;  
  16.                 N--;  
  17.             }  
  18.         }  
  19.     }  
  20.    
  21.     ......    
  22. }    

        這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

        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來實現的,以下所示:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public class WindowManagerService extends IWindowManager.Stub    
  2.         implements Watchdog.Monitor {    
  3.     ......    
  4.     
  5.     public void moveAppToken(int index, IBinder token) {  
  6.         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,  
  7.                 "moveAppToken()")) {  
  8.             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");  
  9.         }  
  10.   
  11.         synchronized(mWindowMap) {  
  12.             ......  
  13.             final AppWindowToken wtoken = findAppWindowToken(token);  
  14.             if (wtoken == null || !mAppTokens.remove(wtoken)) {  
  15.                 ......  
  16.                 return;  
  17.             }  
  18.             mAppTokens.add(index, wtoken);  
  19.             ......  
  20.   
  21.             final long origId = Binder.clearCallingIdentity();  
  22.             ......  
  23.             if (tmpRemoveAppWindowsLocked(wtoken)) {  
  24.                 ......  
  25.                 reAddAppWindowsLocked(findWindowOffsetLocked(index), wtoken);  
  26.                 ......  
  27.                 updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);  
  28.                 mLayoutNeeded = true;  
  29.                 performLayoutAndPlaceSurfacesLocked();  
  30.             }  
  31.             Binder.restoreCallingIdentity(origId);  
  32.         }  
  33.     }  
  34.    
  35.     ......    
  36. }    

        這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

        參數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來得到的,它的實現以下所示:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public class WindowManagerService extends IWindowManager.Stub    
  2.         implements Watchdog.Monitor {    
  3.     ......    
  4.   
  5.     /** 
  6.      * Z-ordered (bottom-most first) list of all Window objects. 
  7.      */  
  8.     final ArrayList<WindowState> mWindows = new ArrayList<WindowState>();  
  9.     ......  
  10.     
  11.     private int findWindowOffsetLocked(int tokenPos) {  
  12.         final int NW = mWindows.size();  
  13.   
  14.         if (tokenPos >= mAppTokens.size()) {  
  15.             int i = NW;  
  16.             while (i > 0) {  
  17.                 i--;  
  18.                 WindowState win = mWindows.get(i);  
  19.                 if (win.getAppToken() != null) {  
  20.                     return i+1;  
  21.                 }  
  22.             }  
  23.         }  
  24.   
  25.         while (tokenPos > 0) {  
  26.             // Find the first app token below the new position that has  
  27.             // a window displayed.  
  28.             final AppWindowToken wtoken = mAppTokens.get(tokenPos-1);  
  29.             ......  
  30.             if (wtoken.sendingToBottom) {  
  31.                 ......  
  32.                 tokenPos--;  
  33.                 continue;  
  34.             }  
  35.             int i = wtoken.windows.size();  
  36.             while (i > 0) {  
  37.                 i--;  
  38.                 WindowState win = wtoken.windows.get(i);  
  39.                 int j = win.mChildWindows.size();  
  40.                 while (j > 0) {  
  41.                     j--;  
  42.                     WindowState cwin = win.mChildWindows.get(j);  
  43.                     if (cwin.mSubLayer >= 0) {  
  44.                         for (int pos=NW-1; pos>=0; pos--) {  
  45.                             if (mWindows.get(pos) == cwin) {  
  46.                                 ......  
  47.                                 return pos+1;  
  48.                             }  
  49.                         }  
  50.                     }  
  51.                 }  
  52.                 for (int pos=NW-1; pos>=0; pos--) {  
  53.                     if (mWindows.get(pos) == win) {  
  54.                         ......  
  55.                         return pos+1;  
  56.                     }  
  57.                 }  
  58.             }  
  59.             tokenPos--;  
  60.         }  
  61.   
  62.         return 0;  
  63.     }  
  64.    
  65.     ......    
  66. }    

        這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

        參數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來實現的,以下所示:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public class WindowManagerService extends IWindowManager.Stub    
  2.         implements Watchdog.Monitor {    
  3.     ......    
  4.     
  5.     public void moveAppTokensToTop(List<IBinder> tokens) {  
  6.         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,  
  7.                 "moveAppTokensToTop()")) {  
  8.             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");  
  9.         }  
  10.   
  11.         final long origId = Binder.clearCallingIdentity();  
  12.         synchronized(mWindowMap) {  
  13.             removeAppTokensLocked(tokens);  
  14.             final int N = tokens.size();  
  15.             for (int i=0; i<N; i++) {  
  16.                 AppWindowToken wt = findAppWindowToken(tokens.get(i));  
  17.                 if (wt != null) {  
  18.                     mAppTokens.add(wt);  
  19.                     if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {  
  20.                         mToTopApps.remove(wt);  
  21.                         mToBottomApps.remove(wt);  
  22.                         mToTopApps.add(wt);  
  23.                         wt.sendingToBottom = false;  
  24.                         wt.sendingToTop = true;  
  25.                     }  
  26.                 }  
  27.             }  
  28.   
  29.             if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET) {  
  30.                 moveAppWindowsLocked(tokens, mAppTokens.size());  
  31.             }  
  32.         }  
  33.         Binder.restoreCallingIdentity(origId);  
  34.     }  
  35.    
  36.     ......    
  37. }    

        這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

        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來實現的,以下所示:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public class WindowManagerService extends IWindowManager.Stub    
  2.         implements Watchdog.Monitor {    
  3.     ......    
  4.     
  5.     public void moveAppTokensToBottom(List<IBinder> tokens) {  
  6.         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,  
  7.                 "moveAppTokensToBottom()")) {  
  8.             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");  
  9.         }  
  10.   
  11.         final long origId = Binder.clearCallingIdentity();  
  12.         synchronized(mWindowMap) {  
  13.             removeAppTokensLocked(tokens);  
  14.             final int N = tokens.size();  
  15.             int pos = 0;  
  16.             for (int i=0; i<N; i++) {  
  17.                 AppWindowToken wt = findAppWindowToken(tokens.get(i));  
  18.                 if (wt != null) {  
  19.                     mAppTokens.add(pos, wt);  
  20.                     if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {  
  21.                         mToTopApps.remove(wt);  
  22.                         mToBottomApps.remove(wt);  
  23.                         mToBottomApps.add(i, wt);  
  24.                         wt.sendingToTop = false;  
  25.                         wt.sendingToBottom = true;  
  26.                     }  
  27.                     pos++;  
  28.                 }  
  29.             }  
  30.   
  31.             if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET) {  
  32.                 moveAppWindowsLocked(tokens, 0);  
  33.             }  
  34.         }  
  35.         Binder.restoreCallingIdentity(origId);  
  36.     }  
  37.    
  38.     ......    
  39. }    

        這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

        WindowManagerService類的成員函數moveAppTokensToBottom能夠同時將一組AppWindowToken移至底 端。將一組AppWindowToken移至底端與將一組AppWindowToken移至頂端的實現是相似的,只不過是移動的方向相反而已。因 此,WindowManagerService類的成員函數moveAppTokensToBottom的實現能夠參考前面所分析的成員函數 moveAppTokensToTop的實現,這裏再也不詳述。

        6. 增長WindowToken

        從圖1能夠知道,若是一個WindowState對象不是與一個AppWindowToken對象對應的,那麼它就必需要與一個WindowToken對 象對應。例如,用來描述輸入法窗口和壁紙窗口的WindowState對象對應的就是WindowToken對象,而不是AppWindowToken對 象,由於它們不是Activity類型的窗口。

        輸入法窗口和壁紙窗口分別是由輸入法管理服務InputMethodManagerService和壁紙管理服務 WallpaperManagerService調用WindowManagerService類的成員函數addWindowToken來增長對應的 WindowToken對象的,以下所示:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public class WindowManagerService extends IWindowManager.Stub    
  2.         implements Watchdog.Monitor {    
  3.     ......    
  4.     
  5.     public void addWindowToken(IBinder token, int type) {  
  6.         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,  
  7.                 "addWindowToken()")) {  
  8.             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");  
  9.         }  
  10.   
  11.         synchronized(mWindowMap) {  
  12.             WindowToken wtoken = mTokenMap.get(token);  
  13.             if (wtoken != null) {  
  14.                 Slog.w(TAG, "Attempted to add existing input method token: " + token);  
  15.                 return;  
  16.             }  
  17.             wtoken = new WindowToken(token, type, true);  
  18.             mTokenMap.put(token, wtoken);  
  19.             mTokenList.add(wtoken);  
  20.             if (type == TYPE_WALLPAPER) {  
  21.                 mWallpaperTokens.add(wtoken);  
  22.             }  
  23.         }  
  24.     }  
  25.    
  26.     ......    
  27. }  

        這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

        調用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相關的邏輯,以下所示:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public class WindowManagerService extends IWindowManager.Stub    
  2.         implements Watchdog.Monitor {    
  3.     ......    
  4.     
  5.     public int addWindow(Session session, IWindow client,  
  6.             WindowManager.LayoutParams attrs, int viewVisibility,  
  7.             Rect outContentInsets, InputChannel outInputChannel) {  
  8.         ......  
  9.   
  10.         synchronized(mWindowMap) {  
  11.             ......  
  12.   
  13.             boolean addToken = false;  
  14.             WindowToken token = mTokenMap.get(attrs.token);  
  15.             if (token == null) {  
  16.                 if (attrs.type >= FIRST_APPLICATION_WINDOW  
  17.                         && attrs.type <= LAST_APPLICATION_WINDOW) {  
  18.                     ......  
  19.                     return WindowManagerImpl.ADD_BAD_APP_TOKEN;  
  20.                 }  
  21.                 if (attrs.type == TYPE_INPUT_METHOD) {  
  22.                     ......  
  23.                     return WindowManagerImpl.ADD_BAD_APP_TOKEN;  
  24.                 }  
  25.                 if (attrs.type == TYPE_WALLPAPER) {  
  26.                     ......  
  27.                     return WindowManagerImpl.ADD_BAD_APP_TOKEN;  
  28.                 }  
  29.                 token = new WindowToken(attrs.token, -1, false);  
  30.                 addToken = true;  
  31.             }  
  32.   
  33.             ......  
  34.   
  35.             if (addToken) {  
  36.                 mTokenMap.put(attrs.token, token);  
  37.                 mTokenList.add(token);  
  38.             }  
  39.    
  40.             ......  
  41.         }  
  42.   
  43.         ......  
  44.     }  
  45.            
  46.     ......    
  47. }  

        這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

        若是參數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來實現的,以下所示:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public class WindowManagerService extends IWindowManager.Stub    
  2.         implements Watchdog.Monitor {    
  3.     ......    
  4.     
  5.     public void removeWindowToken(IBinder token) {  
  6.         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,  
  7.                 "removeWindowToken()")) {  
  8.             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");  
  9.         }  
  10.   
  11.         final long origId = Binder.clearCallingIdentity();  
  12.         synchronized(mWindowMap) {  
  13.             WindowToken wtoken = mTokenMap.remove(token);  
  14.             mTokenList.remove(wtoken);  
  15.             if (wtoken != null) {  
  16.                 boolean delayed = false;  
  17.                 if (!wtoken.hidden) {  
  18.                     wtoken.hidden = true;  
  19.   
  20.                     final int N = wtoken.windows.size();  
  21.                     boolean changed = false;  
  22.   
  23.                     for (int i=0; i<N; i++) {  
  24.                         WindowState win = wtoken.windows.get(i);  
  25.   
  26.                         if (win.isAnimating()) {  
  27.                             delayed = true;  
  28.                         }  
  29.   
  30.                         if (win.isVisibleNow()) {  
  31.                             applyAnimationLocked(win,  
  32.                                     WindowManagerPolicy.TRANSIT_EXIT, false);  
  33.                             changed = true;  
  34.                         }  
  35.                     }  
  36.   
  37.                     if (changed) {  
  38.                         mLayoutNeeded = true;  
  39.                         performLayoutAndPlaceSurfacesLocked();  
  40.                         updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);  
  41.                     }  
  42.   
  43.                     if (delayed) {  
  44.                         mExitingTokens.add(wtoken);  
  45.                     } else if (wtoken.windowType == TYPE_WALLPAPER) {  
  46.                         mWallpaperTokens.remove(wtoken);  
  47.                     }  
  48.                 }  
  49.   
  50.                 ......  
  51.             } else {  
  52.                 Slog.w(TAG, "Attempted to remove non-existing token: " + token);  
  53.             }  
  54.         }  
  55.         Binder.restoreCallingIdentity(origId);  
  56.     }  
  57.            
  58.     ......    
  59. }  

        這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

        調用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來實現的,以下所示:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public class WindowManagerService extends IWindowManager.Stub    
  2.         implements Watchdog.Monitor {    
  3.     ......    
  4.   
  5.     /** 
  6.      * Mapping from an IWindow IBinder to the server's Window object. 
  7.      * This is also used as the lock for all of our state. 
  8.      */  
  9.     final HashMap<IBinder, WindowState> mWindowMap = new HashMap<IBinder, WindowState>();  
  10.     ......  
  11.   
  12.     /** 
  13.      * Z-ordered (bottom-most first) list of all Window objects. 
  14.      */  
  15.     final ArrayList<WindowState> mWindows = new ArrayList<WindowState>();  
  16.     ......  
  17.     
  18.     public int addWindow(Session session, IWindow client,    
  19.             WindowManager.LayoutParams attrs, int viewVisibility,    
  20.             Rect outContentInsets, InputChannel outInputChannel) {    
  21.         ......    
  22.     
  23.         WindowState win = null;    
  24.     
  25.         synchronized(mWindowMap) {    
  26.             ......    
  27.   
  28.             win = new WindowState(session, client, token,    
  29.                     attachedWindow, attrs, viewVisibility);    
  30.             ......    
  31.     
  32.             mWindowMap.put(client.asBinder(), win);    
  33.             ......  
  34.   
  35.             if (attrs.type == TYPE_INPUT_METHOD) {  
  36.                 mInputMethodWindow = win;  
  37.                 addInputMethodWindowToListLocked(win);  
  38.                 ......  
  39.             } else if (attrs.type == TYPE_INPUT_METHOD_DIALOG) {  
  40.                 mInputMethodDialogs.add(win);  
  41.                 addWindowToListInOrderLocked(win, true);  
  42.                 adjustInputMethodDialogsLocked();  
  43.                 ......  
  44.             } else {  
  45.                 addWindowToListInOrderLocked(win, true);  
  46.                 if (attrs.type == TYPE_WALLPAPER) {  
  47.                     .......  
  48.                     adjustWallpaperWindowsLocked();  
  49.                 } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {  
  50.                     adjustWallpaperWindowsLocked();  
  51.                 }  
  52.             }  
  53.   
  54.             ......  
  55.         }  
  56.   
  57.         ......  
  58.     }  
  59.   
  60.     ......  
  61. }  

        這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

        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的實現比較複雜,咱們先列出它的框架,而後再詳細分析它的實現,以下所示:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public class WindowManagerService extends IWindowManager.Stub    
  2.         implements Watchdog.Monitor {    
  3.     ......    
  4.   
  5.     private void addWindowToListInOrderLocked(WindowState win, boolean addToToken) {  
  6.         final IWindow client = win.mClient;  
  7.         final WindowToken token = win.mToken;  
  8.         final ArrayList<WindowState> localmWindows = mWindows;  
  9.   
  10.         final int N = localmWindows.size();  
  11.         final WindowState attached = win.mAttachedWindow;  
  12.         int i;  
  13.         if (attached == null) {  
  14.             //CASE 1:要增長的窗口win沒有附加在其它窗口上  
  15.             int tokenWindowsPos = token.windows.size();  
  16.             if (token.appWindowToken != null) {  
  17.                 //CASE 1.1:要增長的窗口win是一個Activity窗口  
  18.                 int index = tokenWindowsPos-1;  
  19.                 if (index >= 0) {  
  20.                     //CASE 1.1.1:用來要增長的窗口win的令牌token已存在其它窗口  
  21.                     ......  
  22.                 } else {  
  23.                     //CASE 1.1.2:用來要增長的窗口win的令牌token還沒有存在任何窗口  
  24.                     ......  
  25.                 }  
  26.             } else {  
  27.                 //CASE 1.2:要增長的窗口win不是一個Activity窗口  
  28.                 ......  
  29.             }  
  30.             if (addToToken) {  
  31.                 token.windows.add(tokenWindowsPos, win);  
  32.             }  
  33.         } else {  
  34.             //CASE 2:要增長的窗口win附加在窗口attached上  
  35.             ......  
  36.         }  
  37.   
  38.         if (win.mAppToken != null && addToToken) {  
  39.             win.mAppToken.allAppWindows.add(win);  
  40.         }  
  41.     }  
  42.   
  43.     ......  
  44. }  

        這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

        咱們首先分析一下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.1CASE 1.1.2CASE 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對應的代碼爲:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. if (win.mAttrs.type == TYPE_BASE_APPLICATION) {  
  2.     // Base windows go behind everything else.  
  3.     placeWindowBefore(token.windows.get(0), win);  
  4.     tokenWindowsPos = 0;  
  5. else {  
  6.     AppWindowToken atoken = win.mAppToken;  
  7.     if (atoken != null &&  
  8.             token.windows.get(index) == atoken.startingWindow) {  
  9.         placeWindowBefore(token.windows.get(index), win);  
  10.         tokenWindowsPos--;  
  11.     } else {  
  12.         int newIdx =  findIdxBasedOnAppTokens(win);  
  13.         if(newIdx != -1) {  
  14.             //there is a window above this one associated with the same  
  15.             //apptoken note that the window could be a floating window  
  16.             //that was created later or a window at the top of the list of  
  17.             //windows associated with this token.  
  18.             ......  
  19.             localmWindows.add(newIdx+1, win);  
  20.             mWindowsChanged = true;  
  21.         }  
  22.     }  
  23. }  

        這段代碼又分爲三種狀況來將參數win所描述的一個WindowState對象添加到窗口堆棧中:

        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對應的代碼爲:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. // Figure out where the window should go, based on the  
  2. // order of applications.  
  3. final int NA = mAppTokens.size();  
  4. WindowState pos = null;  
  5. for (i=NA-1; i>=0; i--) {  
  6.     AppWindowToken t = mAppTokens.get(i);  
  7.     if (t == token) {  
  8.         i--;  
  9.         break;  
  10.     }  
  11.   
  12.     // We haven't reached the token yet; if this token  
  13.     // is not going to the bottom and has windows, we can  
  14.     // use it as an anchor for when we do reach the token.  
  15.     if (!t.sendingToBottom && t.windows.size() > 0) {  
  16.         pos = t.windows.get(0);  
  17.     }  
  18. }  
  19. // We now know the index into the apps.  If we found  
  20. // an app window above, that gives us the position; else  
  21. // we need to look some more.  
  22. if (pos != null) {  
  23.     // Move behind any windows attached to this one.  
  24.     WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());  
  25.     if (atoken != null) {  
  26.         final int NC = atoken.windows.size();  
  27.         if (NC > 0) {  
  28.             WindowState bottom = atoken.windows.get(0);  
  29.             if (bottom.mSubLayer < 0) {  
  30.                 pos = bottom;  
  31.             }  
  32.         }  
  33.     }  
  34.     placeWindowBefore(pos, win);  
  35. else {  
  36.     // Continue looking down until we find the first  
  37.     // token that has windows.  
  38.     while (i >= 0) {  
  39.         AppWindowToken t = mAppTokens.get(i);  
  40.         final int NW = t.windows.size();  
  41.         if (NW > 0) {  
  42.             pos = t.windows.get(NW-1);  
  43.             break;  
  44.         }  
  45.         i--;  
  46.     }  
  47.     if (pos != null) {  
  48.         // Move in front of any windows attached to this  
  49.         // one.  
  50.         WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());  
  51.         if (atoken != null) {  
  52.             final int NC = atoken.windows.size();  
  53.             if (NC > 0) {  
  54.                 WindowState top = atoken.windows.get(NC-1);  
  55.                 if (top.mSubLayer >= 0) {  
  56.                     pos = top;  
  57.                 }  
  58.             }  
  59.         }  
  60.         placeWindowAfter(pos, win);  
  61.         placeWindowAfter(pos, win);  
  62.     } else {  
  63.         // Just search for the start of this layer.  
  64.         final int myLayer = win.mBaseLayer;  
  65.         for (i=0; i<N; i++) {  
  66.             WindowState w = localmWindows.get(i);  
  67.             if (w.mBaseLayer > myLayer) {  
  68.                 break;  
  69.             }  
  70.         }  
  71.         ......  
  72.         localmWindows.add(i, win);  
  73.         mWindowsChanged = true;  
  74.     }  
  75. }  

        這段代碼要能冠軍WindowManagerService服務內部的一個AppWindowToken列表mAppTokens來在窗口堆棧中找到一個參數位置來保存參數win所描述的一個WindowState對象。

        最上面的一個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對應的代碼爲:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. // Figure out where window should go, based on layer.  
  2. final int myLayer = win.mBaseLayer;  
  3. for (i=N-1; i>=0; i--) {  
  4.     if (localmWindows.get(i).mBaseLayer <= myLayer) {  
  5.         i++;  
  6.         break;  
  7.     }  
  8. }  
  9. if (i < 0) i = 0;  
  10. ......  
  11. localmWindows.add(i, win);  
  12. mWindowsChanged = true;  

        因爲這時候在窗口堆棧中是沒有參考位置來保存參數win所描述的WindowState對象的,所以,這段代碼就只能經過參數win所描述的 WindowState對象的Z軸位置,即它的成員變量mBaseLayer的值來在窗口堆棧中找到一個合適的位置了,如這段代碼中的for循環所示。由 於窗口堆棧中的WindowState對象是按照它們的Z軸位置由小到大的順序來排列的,所以,這段代碼中的for循環只要從上到下找到一個 WindowState對象,它的Z軸位置小於或者等於參數win所描述的WindowState對象的Z軸位置,那麼該WindowState對象在窗 口堆棧中的位置i就能夠用插入參數win所描述的WindowState對象了。

        CASE 2對應的代碼爲:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. // Figure out this window's ordering relative to the window  
  2. // it is attached to.  
  3. final int NA = token.windows.size();  
  4. final int sublayer = win.mSubLayer;  
  5. int largestSublayer = Integer.MIN_VALUE;  
  6. WindowState windowWithLargestSublayer = null;  
  7. for (i=0; i<NA; i++) {  
  8.     WindowState w = token.windows.get(i);  
  9.     final int wSublayer = w.mSubLayer;  
  10.     if (wSublayer >= largestSublayer) {  
  11.         largestSublayer = wSublayer;  
  12.         windowWithLargestSublayer = w;  
  13.     }  
  14.     if (sublayer < 0) {  
  15.         // For negative sublayers, we go below all windows  
  16.         // in the same sublayer.  
  17.         if (wSublayer >= sublayer) {  
  18.             if (addToToken) {  
  19.                 token.windows.add(i, win);  
  20.             }  
  21.             placeWindowBefore(  
  22.                 wSublayer >= 0 ? attached : w, win);  
  23.             break;  
  24.         }  
  25.     } else {  
  26.         // For positive sublayers, we go above all windows  
  27.         // in the same sublayer.  
  28.         if (wSublayer > sublayer) {  
  29.             if (addToToken) {  
  30.                 token.windows.add(i, win);  
  31.             }  
  32.             placeWindowBefore(w, win);  
  33.             break;  
  34.         }  
  35.     }  
  36. }  
  37. if (i >= NA) {  
  38.     if (addToToken) {  
  39.         token.windows.add(win);  
  40.     }  
  41.     if (sublayer < 0) {  
  42.         placeWindowBefore(attached, win);  
  43.     } else {  
  44.         placeWindowAfter(largestSublayer >= 0  
  45.                          ? windowWithLargestSublayer  
  46.                          : attached,  
  47.                          win);  
  48.     }  
  49. }  

        這段代碼要將參數win所描述的WindowState對象附加在變量attached所描述的WindowState對象的上面或者下面,取決於它的成員變量mSubLayer的值是大於0仍是小於0。咱們分四種狀況來考慮。

        第一種狀況是參數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來實現的,以下所示:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public class WindowManagerService extends IWindowManager.Stub    
  2.         implements Watchdog.Monitor {    
  3.     ......    
  4.     
  5.     private int tmpRemoveWindowLocked(int interestingPos, WindowState win) {  
  6.         int wpos = mWindows.indexOf(win);  
  7.         if (wpos >= 0) {  
  8.             if (wpos < interestingPos) interestingPos--;  
  9.             ......  
  10.             mWindows.remove(wpos);  
  11.             mWindowsChanged = true;  
  12.             int NC = win.mChildWindows.size();  
  13.             while (NC > 0) {  
  14.                 NC--;  
  15.                 WindowState cw = win.mChildWindows.get(NC);  
  16.                 int cpos = mWindows.indexOf(cw);  
  17.                 if (cpos >= 0) {  
  18.                     if (cpos < interestingPos) interestingPos--;  
  19.                     ......  
  20.                     mWindows.remove(cpos);  
  21.                 }  
  22.             }  
  23.         }  
  24.         return interestingPos;  
  25.     }  
  26.     
  27.     ......    
  28. }    

        這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

        WindowManagerService類的成員函數tmpRemoveWindowLocked將參數win所描述的窗口及其子窗口從WindowManagerService服務內部的窗口堆棧中刪除,即從 WindowManagerService類的成員變量mWindows所描述的一個ArrayList中刪除。

        若是每個被刪除的窗口在窗口堆棧中的位置比參數interestingPos的值小,那麼WindowManagerService類的成員函數tmpRemoveWindowLocked還會將參數interestingPos的值減小1,這至關因而計算當刪除參數win所描述的窗口及其子窗口以後,原來位於窗口堆棧中第interestingPos個位置的窗口如今位於窗口堆棧的位置,這個位置最終會做爲WindowManagerService類的成員函數tmpRemoveWindowLocked的返回值。

       11. 在指定位置增長WindowState

       在指定位置增長WindowState是經過調用WindowManagerService類的成員函數reAddWindowLocked來實現的,以下所示:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     private final int reAddWindowLocked(int index, WindowState win) {  
  6.         final int NCW = win.mChildWindows.size();  
  7.         boolean added = false;  
  8.         for (int j=0; j<NCW; j++) {  
  9.             WindowState cwin = win.mChildWindows.get(j);  
  10.             if (!added && cwin.mSubLayer >= 0) {  
  11.                 ......  
  12.                 mWindows.add(index, win);  
  13.                 index++;  
  14.                 added = true;  
  15.             }  
  16.             ......  
  17.             mWindows.add(index, cwin);  
  18.             index++;  
  19.         }  
  20.         if (!added) {  
  21.             ......  
  22.             mWindows.add(index, win);  
  23.             index++;  
  24.         }  
  25.         mWindowsChanged = true;  
  26.         return index;  
  27.     }  
  28.   
  29.     ......  
  30. }  

        這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

        參數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對象在窗口堆棧中的最高位置是多少

        基於第九、第10和第11這三操做,能夠組合成不少其它的WindowState操做,如接下來的第十二、第1三、第14和第15個操做所示。

        12. 將一個WindowState對象及其全部子WindowState對象增長到窗口堆棧中

         將一個WindowState對象及其全部子WindowState對象增長到窗口堆棧中是經過調用WindowManagerService類的成員函數reAddWindowToListInOrderLocked來實現的,以下所示:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     private void reAddWindowToListInOrderLocked(WindowState win) {  
  6.         addWindowToListInOrderLocked(win, false);  
  7.         // This is a hack to get all of the child windows added as well  
  8.         // at the right position.  Child windows should be rare and  
  9.         // this case should be rare, so it shouldn't be that big a deal.  
  10.         int wpos = mWindows.indexOf(win);  
  11.         if (wpos >= 0) {  
  12.             ......  
  13.             mWindows.remove(wpos);  
  14.             mWindowsChanged = true;  
  15.             reAddWindowLocked(wpos, win);  
  16.         }  
  17.     }  
  18.   
  19.     ......  
  20. }  

        這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

        爲了獲得參數win所描述的WindowState對象的子WindowState對象在窗口堆棧中的起始位置,WindowManagerService 類的成員函數reAddWindowToListInOrderLocked首先將參數win所描述的WindowState對象增長到窗口堆棧中,這是 經過調用前面所分析的成員函數addWindowToListInOrderLocked來實現的,目的是爲了得到它在窗口堆棧的位置。有了這個位置之 後,WindowManagerService類的成員函數reAddWindowToListInOrderLocked就能夠調用前面所分析的成員函數reAddWindowLocked來將WindowState對象及其全部子WindowState對象增長到窗口堆棧中去了,不過在調用以前,要先將參數win所描述的WindowState對象從窗口中堆棧刪除。

        13. 將一個WindowToken對象對應的全部WindowState對象及其子WindowState對象增長到窗口堆棧的指定位置上

         將一個WindowToken對象對應的全部WindowState對象都增長到窗口堆棧中是經過調用WindowManagerService類的成員函數reAddAppWindowsLocked來實現的,以下所示:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     private final int reAddAppWindowsLocked(int index, WindowToken token) {  
  6.         final int NW = token.windows.size();  
  7.         for (int i=0; i<NW; i++) {  
  8.             index = reAddWindowLocked(index, token.windows.get(i));  
  9.         }  
  10.         return index;  
  11.     }  
  12.   
  13.     ......  
  14. }  

        這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

        與參數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來實現的,以下所示:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     private void moveAppWindowsLocked(AppWindowToken wtoken, int tokenPos,  
  6.             boolean updateFocusAndLayout) {  
  7.         // First remove all of the windows from the list.  
  8.         tmpRemoveAppWindowsLocked(wtoken);  
  9.   
  10.         // Where to start adding?  
  11.         int pos = findWindowOffsetLocked(tokenPos);  
  12.   
  13.         // And now add them back at the correct place.  
  14.         pos = reAddAppWindowsLocked(pos, wtoken);  
  15.   
  16.         if (updateFocusAndLayout) {  
  17.             if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) {  
  18.                 assignLayersLocked();  
  19.             }  
  20.             mLayoutNeeded = true;  
  21.             performLayoutAndPlaceSurfacesLocked();  
  22.         }  
  23.     }  
  24.   
  25.     ......  
  26. }  

        這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

        參數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來實現的,以下所示:

[java] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     private void moveAppWindowsLocked(List<IBinder> tokens, int tokenPos) {  
  6.         // First remove all of the windows from the list.  
  7.         final int N = tokens.size();  
  8.         int i;  
  9.         for (i=0; i<N; i++) {  
  10.             WindowToken token = mTokenMap.get(tokens.get(i));  
  11.             if (token != null) {  
  12.                 tmpRemoveAppWindowsLocked(token);  
  13.             }  
  14.         }  
  15.   
  16.         // Where to start adding?  
  17.         int pos = findWindowOffsetLocked(tokenPos);  
  18.   
  19.         // And now add them back at the correct place.  
  20.         for (i=0; i<N; i++) {  
  21.             WindowToken token = mTokenMap.get(tokens.get(i));  
  22.             if (token != null) {  
  23.                 pos = reAddAppWindowsLocked(pos, token);  
  24.             }  
  25.         }  
  26.   
  27.         if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) {  
  28.             assignLayersLocked();  
  29.         }  
  30.         mLayoutNeeded = true;  
  31.         performLayoutAndPlaceSurfacesLocked();  
  32.   
  33.         //dump();  
  34.     }  
  35.   
  36.     ......  
  37. }  

        這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

        這個操做與前面分析的第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服務是如何管理系統中的輸入法窗口和壁紙窗口的,敬請關注!

老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關注!

相關文章
相關標籤/搜索