WindowManagerService (2)认识wms - l-feng/l-feng.github.io GitHub Wiki

一:WMS的窗口管理结构 1:添加窗口的过程:

public int addWindow(Session session, IWindowclient, int seq,

           WindowManager.LayoutParams attrs, int viewVisibility,int displayId

           Rect outContentInsets, InputChannel outInputChannel) {

          // 首先检查权限,没有权限的客户端不能添加窗口

        intres = mPolicy.checkAddPermission(attrs);

         ......

          // 当为某个窗口添加子窗口时,attachedWindow将用来保存父窗口的实例

       WindowState attachedWindow = null;

        //win就是即将被添加的窗口了

       WindowState win = null;

......

        finalint type = attrs.type;

        synchronized(mWindowMap){            ......

           //①获取窗口要添加到的DisplayContent

           /* 在添加窗口时,必须通过displayId参数指定添加到哪一个DisplayContent。

            */

            finalDisplayContent displayContent = getDisplayContentLocked(displayId);

            if(displayContent == null) {

               return WindowManagerGlobal.ADD_INVALID_DISPLAY;

           }

           ......

           // 如果要添加的窗口是另一个的子窗口,就要求父窗口必须已经存在

            // 注意, attrs.type表示了窗口的类型,attrs.token则表示了窗口所隶属的对象

           // 对于子窗口来说,attrs.token表示了父窗口

            if(type >= FIRST_SUB_WINDOW &&.type <= LAST_SUB_WINDOW) {

               attachedWindow = windowForClientLocked(null, attrs.token, false);

               if (attachedWindow == null) {

                   return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;

               }

               //在这里还可以看出WMS要求窗口的层级关系最多为两层

               if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW

                                &&attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {

                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;

               }

           }

              booleanaddToken = false;

           // ②WindowToken出场!根据客户端的attrs.token取出已注册的WindowToken

           WindowToken token = mTokenMap.get(attrs.token);

           // 下面的if语句块初步揭示了WindowToken和窗口之间的关系

            if(token == null) {

               // 对于以下几种类型的窗口,必须通过LayoutParams.token成员为其指定一个已经

               // 添加至WMS的WindowToken

               if (type >= FIRST_APPLICATION_WINDOW

                                && type<= LAST_APPLICATION_WINDOW) {

                   return WindowManagerGlobal.ADD_BAD_APP_TOKEN;

               }

               if (type == TYPE_INPUT_METHOD) {

                   return WindowManagerGlobal.ADD_BAD_APP_TOKEN;

               }

               if (type == TYPE_WALLPAPER) {

                   return WindowManagerGlobal.ADD_BAD_APP_TOKEN;

               }

               if (type == TYPE_DREAM) {

                   return WindowManagerGlobal.ADD_BAD_APP_TOKEN;

               }

               // 其他类型的窗口则不需要事先向WMS添加WindowToken因为WMS会在这里隐式地创

               // 建一个。注意最后一个参数false,这表示此WindowToken由WMS隐式创建。

               token = new WindowToken(this, attrs.token, -1, false);

                addToken = true;

           } else if (type >= FIRST_APPLICATION_WINDOW

                                             &&type <= LAST_APPLICATION_WINDOW) {

               // 对于APPLICATION类型的窗口,要求对应的WindowToken的类型也为APPLICATION

               // 并且是WindowToken的子类:AppWindowToken

               AppWindowToken atoken = token.appWindowToken;

               if (atoken == null) {

                   return WindowManagerImpl.ADD_NOT_APP_TOKEN;

               } else if (atoken.removed) {

                    returnWindowManagerImpl.ADD_APP_EXITING;

               }

               if (type==TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn){

                   return WindowManagerImpl.ADD_STARTING_NOT_NEEDED;

               }            } else if (type == TYPE_INPUT_METHOD) {

               // 对于其他几种类型的窗口也有类似的要求:窗口类型必须与WindowToken的类型一致

               if (token.windowType != TYPE_INPUT_METHOD) {

                   return WindowManagerGlobal.ADD_BAD_APP_TOKEN;

               }

           } else if (type == TYPE_WALLPAPER) {

               if (token.windowType != TYPE_WALLPAPER) {

                   return WindowManagerGlobal.ADD_BAD_APP_TOKEN;

               }

           } else if (type == TYPE_DREAM) {

               if (token.windowType != TYPE_DREAM) {

                   return WindowManagerGlobal.ADD_BAD_APP_TOKEN;

               }

           }

             // ③WMS为要添加的窗口创建了一个WindowState对象

           // 这个对象维护了一个窗口的所有状态信息

            win= new WindowState(this, session, client, token,

            attachedWindow,seq, attrs, viewVisibility, displayContent);  

           ......

           // WindowManagerPolicy出场了。这个函数的调用会调整LayoutParams的一些成员的取值

            mPolicy.adjustWindowParamsLw(win.mAttrs);

              res= mPolicy.prepareAddWindowLw(win, attrs);

            if(res != WindowManagerGlobal.ADD_OKAY) {

               return res;

           }

             // 接下来将刚刚隐式创建的WindowToken添加到mTokenMap中去。通过这行代码应该

              if(addToken) {

               mTokenMap.put(attrs.token, token);

           }

            win.attach();

           // 然后将WindowState对象加入到mWindowMap中

            mWindowMap.put(client.asBinder(),win);

              }

    }

该代码中涉及到了WindowToken和WindowState以及displayContent 之间的关系:

除子窗口外,添加到任何一个窗口的都必须指明所属的WindowToken;窗口在wms中通过一个WindowState实例进行管理,

同时也需要在窗口中申明其所属于的displayContent,以便于确定窗口将被显示到哪一个屏幕上面。

2:理解windowToken

2.1 意义

 1)将属于一个应用组件的窗口组织在一起。应用组件可以是Activity,InputMethod,WallPaper,Dream。

 2)具有令牌的作用。对于应用组件的行为进行规范管理。它是由应用组件或者管理者负责向WMS申明并持有的。

再开始的时候进行权限检查的时候需要SYSTEM_ALERT_WINDOW或INTERNAL_SYSTEM_WINDOW权限才能创建系统类型的窗口。

2.2 声明WindowToken

 只要是一个Binder对象,都可以作为Token向wms作为申明,对于wms客户端来说,Token只是一个Binder对象。

使用addWindowToken()函数声明Token,将会在wms创建一个WindowToken实例,并且添加到 mTokenMap中。

键值为客户端用于声明Token的Binder的实例。

addWindowToken()告诉我们,WindowToken其实有两个含义:

1)对于显示组件(客户端)而言的token,是任意一个Binder的实例,对于显示组件而言(客户端)来说,

仅仅是一个创建窗口的令牌,没有其他的作用。

2)对于wms而言的WindowToken,这是一个在WindowToken类的实例,保存了对应于客户端一侧的Token(Binder实例)

,并以这个Token为键,存储于mTokenMap中。

关于WindowToken在wallpaper以及activiry中的应用,可以查看《深入理解android 卷三》。

3: WindowState理解

mTokenMap 和mWindowMap这两个hashMap维护了WMS中最重要的两类数据,它们的键都是IBinder,

区别是:mTokenMap的键值可能是IAppWindowToken的Bp端(使用addAppToken声明),或者是其他

任意一个binder的Bp端(使用addWindowToken进行声明);而mWindowToken的键值一定是IWindow

的bp端

4:理解displayContent

windowToken是根据窗口之间的逻辑关系进行分组的,而displayContent则是根据窗口的

显示位置将其分组。隶属于同一个的displayContent的窗口将被显示在同一个屏幕上面。每

一个displayContent都有唯一的一个ID。

二:理解窗口的主次顺序

简单的来说,窗口的显示顺序=主序(mBaseLayer)+子序(mSubLayer)。主序主要描述的是

主窗口以及其子窗口在所有窗口的显示位置,子序主要是描述的是一个子窗口在其他兄弟窗

口之间的位置。

主序越大,在窗口和其子窗口的显示位置越靠前,

子序越大,则子窗口相对于其他兄弟窗口的位置越靠前

通过子序和主序计算窗口顺序的时候有两个关键地方:

addWindowToListInOrderLocked()将新建的WindowState按照一定的顺序插入到当前的displayedContent的mWindow列表中

assignlayersLocked()将根据mWindows的存储顺序对于所有的WindowState的主序和子序进行调整。

在进行顺序完成之后,WMS的动画系统通过Surface.setlayer()设置进Surflinger。