美文网首页
Android中WMS的理解与简介

Android中WMS的理解与简介

作者: MadnessXiong | 来源:发表于2020-04-09 01:04 被阅读0次

    1. WMS的概念

    从名字可以看出,window表明它是与窗口相关的,Manager表明它具有管理者的身份。简单来讲,它是窗口管理员。窗口是一个抽象的概念,从用户的角度来讲,它是一个界面。从SufaceFlinger的角度来讲,它是一个Layer,承载着和界面有关的数据和属性。所以它是一个WindowState,用于管理和界面有关的状态。

    WMS也是系统服务,由SystemServer启动。直到关机时才会退出。发生异常时必须重启。

    • 1.1 WMS涉及的元素:

      • WindowManagerPolicy-mPolicy:窗口策略类。

        Android提供了WindowManagerPolicy接口定义Ui显示策略,手机的实现类为PhoneWindowManager。也可以自己实现WindowManagerPolicy来自定义策略。

      窗口策略:这里的initPolicy()初始化的PhoneWindowManager代表的是一种窗口策略。应用到WMS中则代表了Android显示系统所遵循的统一的窗口显示规则。针对不同的产品,UI显示策略通常是不一样的。如手机一般都有Status Bar,但是平板没有。所以要设定不同的策略。

      • ArraySet<Session>-mSessions:主要用于进程间通信,其他应用程序想要和WMS通信就需要经过Session,每个应用程序进程都会对应一个Session,WMS保存这些Session用来记录所有向WMS提出窗口管理服务的客户端。

      • WindowHashMap<IBinder,WindowState>-mWindowMap:WindowState用于保存窗口信息,用来描述一个窗口。mWindowMap其实就是用来保存WMS中各种窗口的集合

      • ArrayList<AppWindowToken>-mFinishedStarting:AppWindowToken为WindowToken的子类,WindowToken主要有2个作用:

        • 窗口令牌,当应用程序想要向WMS申请新创建一个窗口,则需要向WMS出示有效的WindowToken。AppWindowToken主要用来描述应用程序的WindowToken结构,应用程序中每个Activity都对应一个AppWindowToken
        • WindowToken会将同一个组件(比如同一个Activity)的窗口(WindowState)集合在一起,方便管理

        mFinishedStarting就是用于存储已经完成启动的应用程序窗口(比如Activity)的AppWindowToken列表。

      • ArrayList<WindowState>-mResizingWindows:用来存储正在调整大小的窗口列表。

      • WindowAnimator-mAnimator:用于管理窗口的动画以及特效动画

      • H-h:系统的Handler类,用于将任务加入到主线程消息队列中

      • InputManagerService-mInputManager:输入系统的管理者。会对触摸事件进行处理,他会寻找一个合适的窗口来处理触摸返回信息,WMS是窗口的管理者,所以需要持有IMS引用

      • 显示图形的需求

        • Application Window:普通应用程序显示申请所产生的window,和系统窗口相比,它们的窗口层级值比较低。
        • System Window:系统顶部的系统状态栏,壁纸等
        • Sub Window:Toast等弹窗
      • ActivityManagerService:AMS管理者所有的Activity,而Activity的变化通常会带来界面上的改变。那么界面上产生的变化(淡出等动画效果)也要涉及WMS

    • 1.2 WMS包含的功能

      • 窗口的添加与删除:当某个进程由显示需求时,它可以请求WMS添加一个窗口,并于不再需要显示时移除该窗口
      • 启动窗口:当增了一个窗口时,在某些条件下需要添加一个启动窗口
      • 窗口动画:窗口切换时,采用窗口动画可以加强UI特效。可以定制
      • 窗口大小: Android系统支持显示不同大小的窗口,如StatusBar就只是屏幕最顶层的一条Bar,还有对话框,浮窗等
      • 窗口层级:由Z-Order决定,值越大,排序越靠后,会被值小的窗口盖住
      • 事件派发
    • 1.3 内部组织方式

      当一个新的Activity被启动时,它首先需要在AMS中注册,此时AMS会在内部生成一个ActivityRecord来记录这个Activity;另外因为Activity是四大组件中专门用于UI显示的,所以WMS也会对它以WindowState的形式进行记录。

      所以Activity在AMS中的表现形式为ActivityRecord,在WMS中的表现形式为WindowState,其中WMS还有一个变量AppWindowToken和AMS中的ActivityRecord相对应,这样它们就关联了起来。

    2. WMS的启动

    WMS由SystemServer进程启动,在SystemServer的main()中执行了 startOtherServices(),WMS就在这里启动,看下代码:

           private void startOtherServices() {
                         //part1
                   final Watchdog watchdog = Watchdog.getInstance();
                   watchdog.init(context, mActivityManagerService);
                         //part2
                   inputManager = new InputManagerService(context);
                         //part4
                   wm = WindowManagerService.main(context, inputManager,
                           mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                           !mFirstBoot, mOnlyCore, new PhoneWindowManager());
                         //part5
                   ServiceManager.addService(Context.WINDOW_SERVICE, wm);
                   ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
    
    • part1部分初始化了WatchDog,WatchDog用来监控系统的一些关键服务运行的情况,如WMS,它每分钟都会对被监控的系统服务进行检查,如果被监控的服务出现了死锁,则会杀死WatchDog所在进程,也就是SystemServer进程。前面提到过,WMS发生异常时必须重启,就是在这里进行监控
    • part2部分构造了IMS,IMS负责输入消息,WMS需要持有它的引用
    • part3部分通过WindowManagerService.main()构造了一个WMS对象,并传入了构造好的IMS,这样WMS就持有了IMS引用。
    • part5中WMS,IMS注册到了ServiceManager中,这样如果某个客户端想使用它们的功能,就可以去ServiceManager查询,进而可以进行进程间通信

    WMS是通过它自己的main()构造的,继续看这个方法:

           public static WindowManagerService main() {
               DisplayThread.getHandler().runWithScissors(() ->
                       sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
                               onlyCore, policy), 0);
               return sInstance;
           }
    

    在DisplayThread线程中创建了WMS,这个线程是一个单例的前台线程,它用来处理需要低延迟显示的相关操作,并只能由WM,DisplayManager和InputManager实时执行快速操作。由于WMS的优先级更高,所以system_server线程需要等待DisplayThread线程执行完创建WMS的操作,才会继续执行,在这之前它会一直等待。

    再看WMS的构造方法:

           private WindowManagerService() {
                         //持有IMS引用
               mInputManager = inputManager; // Must be before createDisplayContentLocked.
                         //获取DisplayManager
               mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
               //获取Display数组
                         mDisplays = mDisplayManager.getDisplays();
               //遍历Display,将Display封装成DisplayContent,DisplayContent用来描述一块屏幕
               for (Display display : mDisplays) {
                   createDisplayContentLocked(display);
               }
                     //持有AMS引用
               mActivityManager = ActivityManager.getService();
                     //创建mAnimator,用于管理窗口动画
               mAnimator = new WindowAnimator(this);
               //创建窗口策略管理类PhoneWindowManager
                         initPolicy();
               //将WMS添加到WatchDog中
               Watchdog.getInstance().addMonitor(this);
           }
    

    在WMS的构造中进行了一些初始化,持有了AMS,IMS引用,获取了所有管理的屏幕对象DisplayContent,创建了策略管理类PWM,最后将自己添加到WathDog中。再看PWM的初始化,也就是initPolicy():

           private void initPolicy() {
               UiThread.getHandler().runWithScissors(new Runnable() {
                   @Override
                   public void run() {
                       WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
       
                       mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
                   }
               }, 0);
           }
       
    

    PWM的init()运行在UI线程中,它的优先级高于Display线程,因此DisPlay线程要等待它执行完后才会执行。

    总结:

    • 首先在System_server线程中执行了SystemServer的starOtherService(),在starOtherService()中会调用WMS的main(),这个main()会创建WMS,创建的过程在disPlay线程中实现,因WMS的优先级更高,因此System_server线程要等WMS创建完成后,处于等待状态的System_server线程才会被唤醒从而继续执行下面的代码
    • 在WMS的构造中会调用WMS的initPolicy(),在initPolicy()中又会调用PWM的init(),这个init()在UI线程中运行,它的优先级要高于display线程,因此display线程要等到PWM的init()执行完毕后,才会被唤醒执行后面的代码
    • PWM的init()执行完毕后,disPlay线程就完成了WMS的创建,等待的system_server线程就会被唤起继续执行main()之后的逻辑

    3. Window添加过程

    window的添加从WMS的addWindow()开始:

           public int addWindow() {
                         //part1权限检查
               int res = mPolicy.checkAddPermission(attrs, appOp);
       
               synchronized(mWindowMap) {
                         //part2寻找displayContent
               final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
                         //避免重复添加
               if (mWindowMap.containsKey(client.asBinder())) {
                       return WindowManagerGlobal.ADD_DUPLICATE_ADD;
                   }
                     //part3判断是否子窗口
               if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
                        //寻找父窗口
                 parentWindow = windowForClientLocked(null, attrs.token, false);
                }
                //part4获取WindowToken
                WindowToken token = displayContent.getWindowToken(
                           hasParent ? parentWindow.mAttrs.token : attrs.token);
                             //如果有父窗口就将父窗口的type赋值给rootType,如果没有则将当前窗口的type赋值给rootType
                 final int rootType = hasParent ? parentWindow.mAttrs.type : type;
                 
                            if (token == null) {
                //part5如果token为null,则隐式创建token,这说明创建窗口时可以不向WMS提供提供WindowToken。这里的参数false代表token是隐式创建的,不是传进来的
                    token = new WindowToken(this, binder, type, false, displayContent,
                               session.mCanAddInternalSystemWindow);
                   } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
                 //part6如果为应用程序窗口,将windowToken转换为应用程序窗口的AppWindowToken
                 atoken = token.asAppWindowToken();
                  }
                 
                 //part7创建windowState,它存有窗口的所有信息,代表一个窗口
                 final WindowState win = new WindowState(this, session, client, token, parentWindow, appOp[0], seq, attrs, viewVisibility, session.mUid, session.mCanAddInternalSystemWindow);
                  //part8判断请求添加窗口的客户端是否已经死亡
                  if (win.mDeathRecipient == null) {
                       return WindowManagerGlobal.ADD_APP_EXITING;
                   }
                            //part9判断窗口的DisplayContent是否为null
                   if (win.getDisplayContent() == null) {
                       return WindowManagerGlobal.ADD_INVALID_DISPLAY;
                   }
                            //part10根据窗口的type对窗口的LayoutParams成员变量进行修改
                    mPolicy.adjustWindowParamsLw(win.mAttrs);
                  win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
                            //part11准备将窗口添加到系统中
                  res = mPolicy.prepareAddWindowLw(win, attrs);
                    win.attach();
                  //part12将windowState添加到windowMap中
                    mWindowMap.put(client.asBinder(), win);
    
                                //part13将windowstate添加到该windowstate对应的windowtoken中,这样windowtoken中就包含了同一个组件的windowstate
                  win.mToken.addWindow(win);
    
       
               return res;
           }
    
    • part1处进行了权限检查,如果没有权限则不能添加系统窗口,需要在在manifest中注册权限。没有权限就不会执行后续代码,但是Tost这种系统窗口是用户可以创建的,所以这里需要对权限进行控制。
    • part2处通过displayId来获得窗口要添加到哪个DisplayContent上。
    • part3处判断了是否是子窗口,如果是子窗口,那么还要找出父窗口;而且父窗口本身不能是其他窗口的子窗口,否则会添加失败
    • part4处创建windowToken,如果有父窗口则把父窗口的type赋值给rootType,如果没有则把自身的type赋值给rootType
    • part5判断如果token为null,则隐式创建token,这说明创建窗口时可以不向WMS提供提供WindowToken。这里的参数false代表token是隐式创建的,不是传进来的
    • part6如果token不为null且为应用程序窗口,将windowToken转换为应用程序窗口的AppWindowToken
    • part7创建windowState,它存有窗口的所有信息,代表一个窗口
    • part8判断请求添加窗口的客户端是否已经死亡
    • part9判断窗口的DisplayContent是否为null
    • part10根据窗口的type对窗口的LayoutParams成员变量进行修改
    • part11准备将窗口添加到系统中
    • part12将windowState添加到windowMap中
    • part13将windowstate添加到该windowstate对应的windowtoken中,这样windowtoken中就包含了同一个组件的windowstate

    总结:addWindow()主要做了一下事情

    • 对所要添加的窗口进行检查,如果窗口不满足一些条件,就不会执行下面的代码。
    • WindowToken的相关处理,有的窗口类型需要提供windowToken,没有的话就不会执行下面的逻辑,有的则需要WMS隐式创建windowToken
    • WindowState的创建和相关处理,将WindowToken和WindowState相关联
    • 创建和配置DisplayContent,完成窗口添加到系统前到准备工作

    4. Window的删除过程

    窗口的删除从WindowManagerGlobal.removeVIew()发起,看代码:

           public void removeView(View view, boolean immediate) {
               synchronized (mLock) {
                     //获取要删除的view的索引
                   int index = findViewLocked(view, true);
                   //传入索引
                   removeViewLocked(index, immediate);
          }
    

    这里获取了要删除的View的索引,然后调用了removeViewLocked():

           private void removeViewLocked(int index, boolean immediate) {
                 //获取viewRoot
               ViewRootImpl root = mRoots.get(index);
               View view = root.getView();
               if (view != null) {
                   //获取InputMethodManager实例
                   InputMethodManager imm = InputMethodManager.getInstance();
                   if (imm != null) {
                       //结束view的输入法相关逻辑
                       imm.windowDismissed(mViews.get(index).getWindowToken());
                   }
               }
               boolean deferred = root.die(immediate);
           }
    

    这里调用了ViewRootImpl.die():

           boolean die(boolean immediate) {
                 //immediate代表是否需要立即执行,及ViewRootImpl是否在执行performTraversals
               if (immediate && !mIsInTraversal) {
                   doDie();
                   return false;
                }
               mHandler.sendEmptyMessage(MSG_DIE);
               return true;
           }
    

    这里判断了是否需要立即执行,以及ViewRootImpl是否在执行performTraversals,当ViewRootImpl在执行performTraversals时mIsInTraversal为被置为true,所以doDie()执行的条件是ViewRootImpl不执行performTraversals()时,再看doDie():

             void doDie() {
                 //检查线程,判断执行doDie()方法线程是否是创建view的原始线程,如果不是就抛出异常。只有创建view的原始线程才能操作view
               checkThread();
               synchronized (this) {
                        //是否有子view
                   if (mAdded) {
                                     //如果有则销毁子view
                       dispatchDetachedFromWindow();
                   }
                   if (mAdded && !mFirst) {
                                    //如果有子view并且不是第一次被添加
                       destroyHardwareRenderer();
                   }
               }
        
                        
               WindowManagerGlobal.getInstance().doRemoveView(this);
           }
    

    这里做了线程检查,只有创建view的原始线程才能操作view。然后判断是否有子 view有的话则调用dispatchDetachedFromWindow()去销毁,最后调用了WindowManagerGlobal.doRemoveView():

           void doRemoveView(ViewRootImpl root) {
               synchronized (mLock) {
                   final int index = mRoots.indexOf(root);
                   if (index >= 0) {
                       //
                       mRoots.remove(index);
                       mParams.remove(index);
                       final View view = mViews.remove(index);
                       mDyingViews.remove(view);
                   }
               }
           }
    

    将要删除的view从WindowManagerGlobal中维护的列表中移除。

    再回头去看dispatchDetachedFromWindow():

           void dispatchDetachedFromWindow() {
                   mWindowSession.remove(mWindow);
                    }
    
    

    这里调用了WindowSession.remove(),那么这里进行了进程间通讯,最终会调用到WMS的removeWindow():

           void removeWindow(Session session, IWindow client) {
               synchronized(mWindowMap) {
                     //获取WindowState
                   WindowState win = windowForClientLocked(session, client, false);
                   win.removeIfPossible();
               }
           }
    

    这里线获取了WindowState,然后调用了WindowState.removeIfPossible():

           void removeIfPossible() {
               super.removeIfPossible();
               removeIfPossible(false /*keepVisibleDeadWindow*/);
           }
    

    继续看removeIfPossible(false):

           private void removeIfPossible(boolean keepVisibleDeadWindow) {
                //上面省略了一部分代码,主要是进行条件判断,如果满足任何一条则会return,推迟删除操作
               removeImmediately();
           }
    

    在removeIfPossible()中会进行一系列判断,如果满足任何一个条件都会推迟操作,如view在进行动画时等。

    再看removeImmediately():

           void removeImmediately() {
               super.removeImmediately();
                         //如果当前要删除的是StatusBar或者NavigationBar,将这个window从对应的控制器中删除
               mPolicy.removeWindowLw(this);
                     //将对应的session删除
               mSession.windowRemovedLocked();
               //调用WMS进行一些清理工作
               mService.postWindowRemoveCleanupLocked(this);
           }
    

    最后通过removeImmediately()完成了窗口的删除。

    总结:window的删除可以总结为以下几点

    • 检查删除线程的正确性,如果不正确就抛出异常
    • 从ViewRootImpl列表,布局列表和View列表中删除对应的元素
    • 判断是否可以执行删除操作,如果不能就推迟删除操作
    • 执行删除操作,清理和释放与V相关的一切资源

    相关文章

      网友评论

          本文标题:Android中WMS的理解与简介

          本文链接:https://www.haomeiwen.com/subject/liwfmhtx.html