您现在的位置:首页 > 博客 > Android开发 > 正文
Android窗口管理服务WindowManagerService对窗口的组织方式分析
http://www.drovik.com/      2013-2-1 15:42:11      来源:老罗的Android之旅      点击:
       在后面的两篇文章中,我们再详细分析WindowManagerService类的成员函数addInputMethodWindowToListLocked、adjustInputMethodDialogsLocked和adjustWallpaperWindowsLocked的实现,其中,前两者是与输入法窗口相关的,而后者是与壁纸窗口相关的。本文主要关注WindowManagerService类的成员函数addWindowToListInOrderLocked的实现,它会将一个指定的WindowState对象增加到窗口堆栈中的合适位置上去。

        9. 增加WindowState到窗口堆栈

        从前面的分析可以知道,将一个WindowState对象增加到WindowManagerService服务内部中的窗口堆栈,即WindowManagerService类的成员变量mWindows,是通过调用WindowManagerService类的成员函数addWindowToListInOrderLocked来实现的。

       WindowManagerService类的成员函数addWindowToListInOrderLocked的实现比较复杂,我们先列出它的框架,然后再详细分析它的实现,如下所示:

  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 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. }  
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private void addWindowToListInOrderLocked(WindowState win, boolean addToToken) { final IWindow client = win.mClient; final WindowToken token = win.mToken; final ArrayList localmWindows = mWindows; final int N = localmWindows.size(); final WindowState attached = win.mAttachedWindow; int i; if (attached == null) { //CASE 1:要增加的窗口win没有附加在其它窗口上 int tokenWindowsPos = token.windows.size(); if (token.appWindowToken != null) { //CASE 1.1:要增加的窗口win是一个Activity窗口 int index = tokenWindowsPos-1; if (index >= 0) { //CASE 1.1.1:用来要增加的窗口win的令牌token已存在其它窗口 ...... } else { //CASE 1.1.2:用来要增加的窗口win的令牌token尚未存在任何窗口 ...... } } else { //CASE 1.2:要增加的窗口win不是一个Activity窗口 ...... } if (addToToken) { token.windows.add(tokenWindowsPos, win); } } else { //CASE 2:要增加的窗口win附加在窗口attached上 ...... } if (win.mAppToken != null && addToToken) { win.mAppToken.allAppWindows.add(win); } } ...... }        这个函数定义在文件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对应的代码为:

  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. }  
if (win.mAttrs.type == TYPE_BASE_APPLICATION) { // Base windows go behind everything else. placeWindowBefore(token.windows.get(0), win); tokenWindowsPos = 0; } else { AppWindowToken atoken = win.mAppToken; if (atoken != null && token.windows.get(index) == atoken.startingWindow) { placeWindowBefore(token.windows.get(index), win); tokenWindowsPos--; } else { int newIdx = findIdxBasedOnAppTokens(win); if(newIdx != -1) { //there is a window above this one associated with the same //apptoken note that the window could be a floating window //that was created later or a window at the top of the list of //windows associated with this token. ...... localmWindows.add(newIdx+1, win); mWindowsChanged = true; } } }

        这段代码又分为三种情况来将参数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对应的代码为:

  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
  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. }  
// Figure out where the window should go, based on the // order of applications. final int NA = mAppTokens.size(); WindowState pos = null; for (i=NA-1; i>=0; i--) { AppWindowToken t = mAppTokens.get(i); if (t == token) { i--; break; } // We haven't reached the token yet; if this token // is not going to the bottom and has windows, we can // use it as an anchor for when we do reach the token. if (!t.sendingToBottom && t.windows.size() > 0) { pos = t.windows.get(0); } } // We now know the index into the apps. If we found // an app window above, that gives us the position; else // we need to look some more. if (pos != null) { // Move behind any windows attached to this one. WindowToken atoken = mTokenMap.get(pos.mClient.asBinder()); if (atoken != null) { final int NC = atoken.windows.size(); if (NC > 0) { WindowState bottom = atoken.windows.get(0); if (bottom.mSubLayer < 0) { pos = bottom; } } } placeWindowBefore(pos, win); } else { // Continue looking down until we find the first // token that has windows. while (i >= 0) { AppWindowToken t = mAppTokens.get(i); final int NW = t.windows.size(); if (NW > 0) { pos = t.windows.get(NW-1); break; } i--; } if (pos != null) { // Move in front of any windows attached to this // one. WindowToken atoken = mTokenMap.get(pos.mClient.asBinder()); if (atoken != null) { final int NC = atoken.windows.size(); if (NC > 0) { WindowState top = atoken.windows.get(NC-1); if (top.mSubLayer >= 0) { pos = top; } } } placeWindowAfter(pos, win); placeWindowAfter(pos, win); } else { // Just search for the start of this layer. final int myLayer = win.mBaseLayer; for (i=0; i myLayer) { break; } } ...... localmWindows.add(i, win); mWindowsChanged = true; } }        这段代码要能冠军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对应的代码为:

  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;  
// Figure out where window should go, based on layer. final int myLayer = win.mBaseLayer; for (i=N-1; i>=0; i--) { if (localmWindows.get(i).mBaseLayer <= myLayer) { i++; break; } } if (i < 0) i = 0; ...... localmWindows.add(i, win); mWindowsChanged = true;        由于这时候在窗口堆栈中是没有参考位置来保存参数win所描述的WindowState对象的,因此,这段代码就只能通过参数win所描述的WindowState对象的Z轴位置,即它的成员变量mBaseLayer的值来在窗口堆栈中找到一个合适的位置了,如这段代码中的for循环所示。由于窗口堆栈中的WindowState对象是按照它们的Z轴位置由小到大的顺序来排列的,因此,这段代码中的for循环只要从上到下找到一个WindowState对象,它的Z轴位置小于或者等于参数win所描述的WindowState对象的Z轴位置,那么该WindowState对象在窗口堆栈中的位置i就可以用插入参数win所描述的WindowState对象了。

        CASE 2对应的代码为:

  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
  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. }  
// Figure out this window's ordering relative to the window // it is attached to. final int NA = token.windows.size(); final int sublayer = win.mSubLayer; int largestSublayer = Integer.MIN_VALUE; WindowState windowWithLargestSublayer = null; for (i=0; i= largestSublayer) { largestSublayer = wSublayer; windowWithLargestSublayer = w; } if (sublayer < 0) { // For negative sublayers, we go below all windows // in the same sublayer. if (wSublayer >= sublayer) { if (addToToken) { token.windows.add(i, win); } placeWindowBefore( wSublayer >= 0 ? attached : w, win); break; } } else { // For positive sublayers, we go above all windows // in the same sublayer. if (wSublayer > sublayer) { if (addToToken) { token.windows.add(i, win); } placeWindowBefore(w, win); break; } } } if (i >= NA) { if (addToToken) { token.windows.add(win); } if (sublayer < 0) { placeWindowBefore(attached, win); } else { placeWindowAfter(largestSublayer >= 0 ? windowWithLargestSublayer : attached, win); } }        这段代码要将参数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来实现的,如下所示:

  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. }    
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private int tmpRemoveWindowLocked(int interestingPos, WindowState win) { int wpos = mWindows.indexOf(win); if (wpos >= 0) { if (wpos < interestingPos) interestingPos--; ...... mWindows.remove(wpos); mWindowsChanged = true; int NC = win.mChildWindows.size(); while (NC > 0) { NC--; WindowState cw = win.mChildWindows.get(NC); int cpos = mWindows.indexOf(cw); if (cpos >= 0) { if (cpos < interestingPos) interestingPos--; ...... mWindows.remove(cpos); } } } return interestingPos; } ...... }

        这个函数定义在文件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来实现的,如下所示:

  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
  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. }  
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private final int reAddWindowLocked(int index, WindowState win) { final int NCW = win.mChildWindows.size(); boolean added = false; for (int j=0; j= 0) { ...... mWindows.add(index, win); index++; added = true; } ...... mWindows.add(index, cwin); index++; } if (!added) { ...... mWindows.add(index, win); index++; } mWindowsChanged = true; return index; } ...... }

        这个函数定义在文件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对象在窗口堆栈中的最高位置是多少

        基于第9、第10和第11这三操作,可以组合成很多其它的WindowState操作,如接下来的第12、第13、第14和第15个操作所示。

        12. 将一个WindowState对象及其所有子WindowState对象增加到窗口堆栈中

         将一个WindowState对象及其所有子WindowState对象增加到窗口堆栈中是通过调用WindowManagerService类的成员函数reAddWindowToListInOrderLocked来实现的,如下所示:

  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. }  
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private void reAddWindowToListInOrderLocked(WindowState win) { addWindowToListInOrderLocked(win, false); // This is a hack to get all of the child windows added as well // at the right position. Child windows should be rare and // this case should be rare, so it shouldn't be that big a deal. int wpos = mWindows.indexOf(win); if (wpos >= 0) { ...... mWindows.remove(wpos); mWindowsChanged = true; reAddWindowLocked(wpos, win); } } ...... }

        这个函数定义在文件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来实现的,如下所示:

  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
  8.             index = reAddWindowLocked(index, token.windows.get(i));  
  9.         }  
  10.         return index;  
  11.     }  
  12.   
  13.     ......  
  14. }  
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private final int reAddAppWindowsLocked(int index, WindowToken token) { final int NW = token.windows.size(); for (int i=0; i        这个函数定义在文件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来实现的,如下所示:

  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. }  
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private void moveAppWindowsLocked(AppWindowToken wtoken, int tokenPos, boolean updateFocusAndLayout) { // First remove all of the windows from the list. tmpRemoveAppWindowsLocked(wtoken); // Where to start adding? int pos = findWindowOffsetLocked(tokenPos); // And now add them back at the correct place. pos = reAddAppWindowsLocked(pos, wtoken); if (updateFocusAndLayout) { if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) { assignLayersLocked(); } mLayoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); } } ...... }

        这个函数定义在文件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来实现的,如下所示:

  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     private void moveAppWindowsLocked(List 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
  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
  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. }  
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private void moveAppWindowsLocked(List tokens, int tokenPos) { // First remove all of the windows from the list. final int N = tokens.size(); int i; for (i=0; i        这个函数定义在文件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://blog.csdn.net/luoshengyang/article/details/8498908

 

分享到:
发表评论(0)
姓名 *
评论内容 *
验证码 *图片看不清?点击重新得到验证码