Android中WMS的理解与简介

2020-04-09  本文已影响0人  MadnessXiong

1. WMS的概念

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

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

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);

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线程要等待它执行完后才会执行。

总结:

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;
       }

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

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的删除可以总结为以下几点

上一篇 下一篇

猜你喜欢

热点阅读