解析WindowManagerService

解析WindowManagerService

       WindowManagerService(以后简称WMS)它是 WindowManager 的管理者, WMS 无论对于应用开发还是 Framework 开发都是重要的知识点,究其原因是因为 WMS 有很多职责,每个职责都会涉及重要且复杂的系统。 WMS 的职责主要有以下几点。

       1. 窗口管理:负责窗口的启动、添加和删除。另外窗口的大小和层级也是由WMS进行管理的。窗口管理的核心成员有DisplayContent、WindowToken和WindowState。

       2. 窗口动画:在进行窗口切换时,使用窗口动画可以显得更炫一些,窗口动画由 WMS 的动画子系统来负责,动画子系统的管理者为 WindowAnimator。

       3. 输入系统的中转站:通过对窗口的触摸从而产生触摸事件, InputManagerService (IMS )会对触摸事件进行处理,它会寻找一个最合适的窗口来处理触摸反馈信息, WMS 是窗口的管理者,它作为输入系统的中转站再合适不过了。

       4. Surface管理:窗口并不具备绘制的功能,因此每个窗口都需要有一块 Surface 来供自己绘制,为每个窗口分配 Surface 是由 WMS 来完成的。

本文将基于Android8.1.0系统来详细讲解WMS。

    1. WMS 的创建过程

       WMS是在SystemServer进程中创建的,不了解SystemServer进程的可以查看“Android系统启动流程(3) —— 解析SystemServer进程启动过程”这篇文章,我们知道官方把系统服务分为了引导服务、核心服务和其他服务,其中其他服务是一些非紧要和不需要立即启动的服务, WMS 就是其他服务的一种 。我们来查看在SystemServer进程中的 startOtherServices 方法中是如何启动 WMS 的,代码如下所示:

frameworks/base/services/java/com/android/server/SystemServer.java             

    private void startOtherServices() {
        ...
        try {
            ...
            traceBeginAndSlog("InitWatchdog");
            final Watchdog watchdog = Watchdog.getInstance(); // ... 1
            watchdog.init(context, mActivityManagerService);  // ... 2
            traceEnd();

            traceBeginAndSlog("StartInputManagerService");
            inputManager = new InputManagerService(context); // ... 3
            traceEnd();

            traceBeginAndSlog("StartWindowManagerService");
            // WMS needs sensor service ready
            ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
            mSensorServiceStart = null;
            wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore, new PhoneWindowManager()); // ... 4
            ServiceManager.addService(Context.WINDOW_SERVICE, wm); // ... 5
            ServiceManager.addService(Context.INPUT_SERVICE, inputManager); // ... 6
            traceEnd();
            ...
        } catch (RuntimeException e) {
            Slog.e("System", "******************************************");
            Slog.e("System", "************ Failure starting core service", e);
        }

        ...

        traceBeginAndSlog("MakeDisplayReady");
        try {
            wm.displayReady(); // ... 7
        } catch (Throwable e) {
            reportWtf("making display ready", e);
        }
        traceEnd();

        ...

        traceBeginAndSlog("MakeWindowManagerServiceReady");
        try {
            wm.systemReady(); // ... 8
        } catch (Throwable e) {
            reportWtf("making Window Manager Service ready", e);
        }
        traceEnd();

        ...
    }
       在注释 1 、注释2 处分别得到 Watchdog 实 例并对它进行初始化, Watchdog 用来监控系统的一些关 键服务的运行 状况。 在注释3 处创 建了 IMS 类型的对象inputManager   在注释4 处执行了 WMS 的  main 方法 , 其内部会 创建 WMS,需要 注意 的是 main 方法第二个 数传入的值就是 在注释3 处创建的inputManager, WMS 是输入事件 的中转站,其内部包含  IMS 引用并 不意外 在注释5 和注释6处分 别将 WMS 和  IMS 注册到 ServiceManager 中,这样如 果某个 客户 端想要 使用 WMS ,就需要先去 ServiceManager 中查 询信息,然后根据信息与 WMS 的进程建立 通信通路,  客户端就可以使用 WMS 了。在注释7 处用来 初始化 屏幕显示信息,在注释8 处则用 来通知 WMS 系统的初始化工作已经完成,其内部调用了 WindowManagerPolicy 的  systemReady 方法,  接下 来查 看 WMS 的 main 方法,代码 如下所示:
 

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java


    public static WindowManagerService main(final Context context, final InputManagerService im,
            final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
            WindowManagerPolicy policy) {
        DisplayThread.getHandler().runWithScissors(() ->
                sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
                        onlyCore, policy), 0); // ... 1
        return sInstance;
    }

       通过调用 DisplayThread 的 getHandler 方法来得到 DisplayThread 的 Handler 实例。DisplayThread 是一个单例的前台线程,这个线程用来处理需要低延时显示的相关操作, 并只能由 WindowManager、 DisplayManager 和 InputManager 实时执行快速操作 。在注释1处创建了 WMS 实例,这个过程运行在 Runnable 的 run 方法中,而 Runnable 则传到了 DisplayThread 对应 Handler 的 runWithScissors 方法中,说明 WMS 的创建是运行 android.display 中的 。需要注意的是, runWithScissors 方法的第二个参数传入的是0,后面会提到, 下面来查看 Handler 的 runWithScissors 方法,代码如下所示:

frameworks/base/core/java/android/os/Handler.java

    public final boolean runWithScissors(final Runnable r, long timeout) {
        if (r == null) {
            throw new IllegalArgumentException("runnable must not be null");
        }
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout must be non-negative");
        }

        if (Looper.myLooper() == mLooper) { // ... 1
            r.run();
            return true;
        }

        BlockingRunnable br = new BlockingRunnable(r);
        return br.postAndWait(this, timeout);
    }
       在run WithScissors 方法中先对传入的 Runnable 和  timeout 进行了判断,如 Runnable 为  null 或者 timeout 小于 0 则抛出异 常。在注释1处 根据每 个线程只 有一个 Looper 的原理来 判断 当前 线程 (SystemServer 线程,因为WMS的main方法是在SystemServer进程中调用的) 是否是 Handler 所指向的线 程( android. display 线程),如 果是 直接 执行 Runnable 的  run 方法   ,如 果不是 则调用 BlockingRunnable 的 postAndWait 方法,  并将当 前线程的 Runnable作为参数传进去,BlockingRunnable是 Handler的内部类,代码如下所示:
 
frameworks/base/core/java/android/os/Handler.java

    private static final class BlockingRunnable implements Runnable {
        private final Runnable mTask;
        private boolean mDone;

        public BlockingRunnable(Runnable task) {
            mTask = task;
        }

        @Override
        public void run() {
            try {
                mTask.run(); // ... 1
            } finally {
                synchronized (this) {
                    mDone = true;
                    notifyAll();
                }
            }
        }

        public boolean postAndWait(Handler handler, long timeout) {
            if (!handler.post(this)) { // ... 2
                return false;
            }

            synchronized (this) {
                if (timeout > 0) {
                    final long expirationTime = SystemClock.uptimeMillis() + timeout;
                    while (!mDone) {
                        long delay = expirationTime - SystemClock.uptimeMillis();
                        if (delay <= 0) {
                            return false; // timeout
                        }
                        try {
                            wait(delay);
                        } catch (InterruptedException ex) {
                        }
                    }
                } else {
                    while (!mDone) {
                        try {
                            wait(); // ... 3
                        } catch (InterruptedException ex) {
                        }
                    }
                }
            }
            return true;
        }
    }
       在注释2 处将当 前的 BlockingRunnable 添 加到 Handler 的任务队列中。前面 runWithScissors 方法 的第二 个参数为 0 ,因此 timeout 等于 0  ,这样如果 mDone 为  false 的话 会一直 调用注释3 处的 wait 使得当 前线 程( SystemServer 线程 )进入 等待 状态, 那么等待的是哪个线程呢? 在注释1 处执行了传入的 Runnable 的  run 方方法  (运行在 android.display 线程),执行完毕后在 finally 代码块中将 mDone 设置为  true ,并调用 notify All 方法唤醒处 于等 待状态的线程, 这样就不 会继续 调用注释3 处的 wait 方法. 因此得出结论, SystemServer  线程等 待的就是 android.display 线程, 直到 android.display 线程执行完毕再执行 SystemServer 线程,这是 因为 android. display 线 程内部执行了 WMS 的创建,而 WMS 的创建优先级要更 高。接下来开始分析WMS的构造方法,代码如下所示:
 

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java


    private WindowManagerService(Context context, InputManagerService inputManager,
            boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
            WindowManagerPolicy policy) {
       ...
       // Must be before createDisplayContentLocked.
        mInputManager = inputManager;  // ... 1
        ...
        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
        mDisplays = mDisplayManager.getDisplays(); // ... 2
        for (Display display : mDisplays) {
            createDisplayContentLocked(display); // ... 3
        }

        ...

        mActivityManager = ActivityManager.getService(); // ... 4
        ...

        mAnimator = new WindowAnimator(this); // ... 5

        mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
                com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);


        LocalServices.addService(WindowManagerInternal.class, new LocalService());
        initPolicy(); // ... 6

        // Add ourself to the Watchdog monitors.
        Watchdog.getInstance().addMonitor(this); // ... 7

        openSurfaceTransaction();
        try {
            createWatermarkInTransaction();
        } finally {
            closeSurfaceTransaction();
        }

        showEmulatorDisplayOverlayIfNeeded();
    }
       注释1 处用来保存传进来的 IMS ,这样 WMS 就持有了IMS 的引用。在注释2 处通过 Display Manager 的  getDispl ays 方法得到 Disp lay  数组(每个显示设备都有一个 Display 实例), 接着遍历 Disp lay 数组,在注释3 处的 create Displ ayContentLocked 方法 Disp lay 封装成 DisplayContent, DisplayContent 用来描述一 块屏幕。在注释4 处得到 AMS 实例,并赋值给 mActivityManager ,这样 WMS 就持有了 AMS 的引用。在注释5 处创建了 Window Animator, 它用于管理所有的窗口动画。在注释6处初始化了窗口管理策略的接口类 WindowManagerPolicy (WMP ),它用来定义一个窗口策略 所要遵循的通用规范。在注释 7处将自身也就是WMS 通过 addMonitor 方法 添加到 Watc hdog 中, Watchdog 用来监控系统的一些关键服务的运行状况(比如传入的 WMS 的运行状况),这些被监控的服务都会实现 Watchdog.Monitor 接口。 Watchdog 每分钟都会对被监控的系统服务进行检查,如被监控 的系统服务出现了死锁,则 会杀死 Wa tchdog 所在的进程,也就是 SystemServer 进程。查看 注释6处的 initPolicy 方法,如下所示:
 
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
 
    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); // ... 1
            }
        }, 0);
    }
       initPolicy 方法 和此前讲的 WMS 的  main 方法的实现类似,在注释1 处执行了 WMP 的 init 方法, WMP 是 一个接口, init 方法具体在 PhoneWindowManager (PWM )中实现。 PWM 的  init 方法运行在 android.ui 线程中,它的优先级要高于 initPolicy 方法 所在的 android.display 线程,因此  android .disp lay 线程要等 PWM 的 in it 方法 执行完毕后,处于等待状态的 android.display 线程才会被唤醒从而继续执行下面的代码。本文共提到了3 个线程,分别是SystemServer、   android.displ ay 和  android.ui ,为了便于理解,下面给出这三个线程之间的关系如图 1 所 示。

 

图1  SystemServer、android.display和android.ui之间的关系

 

 

    2. WMS的重要成员

       1. mPolicy:WindowManagerPolicy

       mPolicy是WindowManagerPolicy(WMP)类型的变量。WMP是窗口管理策略的接口类,用来定义一个窗口策略所要遵循的通用规范,并提供了 WindowManager 所有的特定的 UI 行为 。它的具体实现类为 PhoneWindowManager ,这个实现类在 WMS 创建时被创建。 WMP 允许定制窗口层级和特殊窗口类型以及关键的调度和布局。

       2. mSessions: ArraySet

       mSessions 是 ArraySet 类型的变量,元素类型为 Session ,它主要用于进程间通信,其他的应用程序进程想要和 WMS 进程进行通信就需要经过 Session ,并且每个应用程序进程都会对应一个 Session, WMS 保存这些 Session 用来记录所有向 WMS 提出窗口管理服务的客户端。

       3. mWindowMap: WindowHashMap
 
       mWindowMap 是  WindowHashMap 类型的变量, WindowHashMap 继承了 HashM ap, 它限制了 HashMap 的  key 值的类型为 I Binder, value 值的类型为 WindowState 。WindowState 用于保存窗口的信息,在 WMS 中它用来描述一 个窗口。综上得出结论, mWindowMap 是用来保存 WMS 中各种窗口的集合。
 
       4. mFinishedStarting: Arraylist
 
       mFinishedStarting 是  Array List 类型的变量 ,元素类型为 App WindowToken ,它是 WindowToken 的子类。要想理解 mFinishedStarting 的含义,需要先了解 WindowToken 是什 么。 WindowToken 主要有两个作用:
       (1)可以理解为窗口令牌,当应用程序想要向 WMS 申请新创建一 个窗口,则 需要向 WMS 出示有效的 WindowToken。 App WindowToken 作为 WindowToken 的子类,主要用来描述应用程序的 WindowToken 结构, 应用程序中每个 Activity 都对应一个 AppWindowToken。
       (2) WindowToken 会将同一 个组件(比如同 Activity )的窗口( WindowState )集合在一起,方便管理。
        mFinishedStarting 就是用于存储已经完成启动的应用程序窗口(比如Ac tivity )的 App WindowToken 的列表。除了 mFinishedStarting 外,还有类似的 mFinishedEarly Anim 和 mWindowReplacementTimeouts ,其中 mFinishedEarly Anim 存储了已经完成窗口绘制并且不 需要展示任何已保存 surface 的应用程序窗口的 App WindowToken 。mWindowReplacementTimeout 存储了等待更换的应用程序窗口的 App WindowToken ,如果 更换不及时,旧窗口就需要被处理。
 
       5. mResizingWindows: Arraylist
 
       mResizingWindows 是  Array List 类型的变量 ,元素类型为 WindowState 。 mResizingWindows 是用来存储正在调整大小的窗口的列表。与 mResizing Windows 类似的还有 mPendingRemove 、mDestroySurface 、 mDestroyPreservedSurface 等,其中 mPendingRemove 是在内存耗尽时设置的,里面存有需要强制删除的窗口, mDestroySurface 里面存有需要被销毁的 Surface, mDestroyPreservedSurface 里面存有窗口需要保存的等待销毁的 Surface, 为什么窗口要保存这些 Surface ?这是因为当窗口 Surface 变 化时 窗口需 要一直 保持旧  Surface,直 到新 Surface 的 第一帧绘 制完成。
 
       6. mAnimator WindowAnimator
 
       mAnimator 是  Window Animator 类型的变量 ,用于管理窗 口的动画以及特效动 画。

 

       7. mH: H
       
       mH 是 H 类型的变量 ,系统的 Handler 类,用于将任务加入到主 线程的消息队列中, 这样代码逻辑就会在主线程中执 行。

 

       8. mlnputManager: lnputManagerService
 
       mlnputManager 是  InputManagerService 类型的变量 ,输入系统的管理者。 InputManagerService (IMS)会 对触摸事 件进行处理,它 会寻找一 个最合适的窗口来处理触摸反馈信息。

 

    3. Window 的添加过程( WMS 处理部分)

       Window的操作分为两大部分,一部分是WindowManger处理部分,另一部分是WMS处理部分,在“解析WindowManager中讲过Window 添加过程的 WindowManager 处理部分,这里我们接着来分析 Window 的添加过程的 WMS 处理部分。无论是系统 口还是 Activity ,它们的 Window 的添加过程都会调用 WMS 的 addWindow方法,代码如下所示

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java


    public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
        int[] appOp = new int[1];
        int res = mPolicy.checkAddPermission(attrs, appOp); // ... 1
        if (res != WindowManagerGlobal.ADD_OKAY) {
            return res;
        }
        ...
        synchronized(mWindowMap) {
            if (!mDisplayReady) {
                throw new IllegalStateException("Display has not been initialialized");
            }

            final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId); // ... 2
            if (displayContent == null) {
                Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
                        + displayId + ".  Aborting.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }
            ...

            if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) { // ... 3
                parentWindow = windowForClientLocked(null, attrs.token, false); // ... 4
                if (parentWindow == null) {
                    Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
                if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
                        && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
                    Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
            }

            ...

            AppWindowToken atoken = null;
            final boolean hasParent = parentWindow != null;
            // Use existing parent window token for child windows since they go in the same token
            // as there parent window so we can apply the same policy on them.
            WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token); // ... 5
            // If this is a child window, we want to apply the same type checking rules as the
            // parent window type.
            final int rootType = hasParent ? parentWindow.mAttrs.type : type; // ... 6

            boolean addToastWindowRequiresToken = false;

            if (token == null) {
                ...

                final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
                token = new WindowToken(this, binder, type, false, displayContent,
                        session.mCanAddInternalSystemWindow); // ... 7
            } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) { // ... 8
                atoken = token.asAppWindowToken(); // ... 9
               
            } 
            ...

            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow); // ... 10
            if (win.mDeathRecipient == null) { // ... 11
                // Client has apparently died, so there is no reason to
                // continue.
                Slog.w(TAG_WM, "Adding window client " + client.asBinder()
                        + " that is dead, aborting.");
                return WindowManagerGlobal.ADD_APP_EXITING;
            }

            if (win.getDisplayContent() == null) { // ... 12
                Slog.w(TAG_WM, "Adding window to Display that has been removed.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }

            mPolicy.adjustWindowParamsLw(win.mAttrs); // ... 13 
            win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));

            res = mPolicy.prepareAddWindowLw(win, attrs); // ... 14
            if (res != WindowManagerGlobal.ADD_OKAY) {
                return res;
            }

            ...

            win.attach();
            mWindowMap.put(client.asBinder(), win); // ... 15
            if (win.mAppOp != AppOpsManager.OP_NONE) {
                int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(),
                        win.getOwningPackage());
                if ((startOpResult != AppOpsManager.MODE_ALLOWED) &&
                        (startOpResult != AppOpsManager.MODE_DEFAULT)) {
                    win.setAppOpVisibilityLw(false);
                }
            }

            final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty();
            win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);

            final AppWindowToken aToken = token.asAppWindowToken();
            if (type == TYPE_APPLICATION_STARTING && aToken != null) {
                aToken.startingWindow = win;
                if (DEBUG_STARTING_WINDOW) Slog.v (TAG_WM, "addWindow: " + aToken
                        + " startingWindow=" + win);
            }

            boolean imMayMove = true;

            win.mToken.addWindow(win); // ... 16
            ...

        }
        ...
        return res;
    }
       WMS 的  addWindow 返回的是 addWindow 的各种状态,比如添加 Window 成功、无效的 display 等状态,这些状态被定义在 WindowManagerGlobal 中  。在注释1 处根据 Window 的属性,调用 WMP 的  checkAddPermission 方法来检查权限, 具 体在 Phone Window Manager 的 checkAddPermission 方法中实现,如 果没有权 限则不 执行后续的代码逻辑。
 
       在注释2 处通过 display 来获得窗口要添加 到哪个 Display Co ntent 上,如 果没有找到 DisplayContent ,则返回 WindowMana gerGloba l.ADD_ INVALID_DISPLAY 这一状态,其 DisplayContent 用来描述一 块屏 幕。
 
       在注释3 处, type 代表一 个窗口的类型,它的数值介于 FIRST_SUB_ WINDOW 和  LAST _SU WINDOW 之间( 1000 ~  1999 ),这个 数值定义在 Window Manager 中,说明这个 窗口是一 个子窗口。
 
       在注释4 处, attrs.token 是 IBinder类 型的对象, windowF orClientLocked 内部 会根据 attrs. token 作为 key 值从 mWindowMap
中得到 该子窗口 的父 窗口。接着对父窗 口进行判断,如 果父窗 n ull 或者 type 的取值 范围不正确则返回错误的状态。
 

       在注释5处通过 displayContent 的 getWindowToken 方法得到 WindowToken 。

       在注释6处如果有父窗口就将父窗口的 type 值赋值给 rootType ,如果没有将当前窗口的 type 值赋值给 rootType 。接下来如果 WindowToken 为 null ,则根据 rootType 或者 type 的值进行区分判断。

       在注释7处隐式创建 WindowToken ,这说明当我们添加窗口时可以不向 WMS 提供 WindowToken ,前提是 rootType 的 type 的值不为前面条件判断筛选的值。 WindowToken 隐式和显式的创建肯定是要加以区分的。注释7处的第4个参数为 false 就代表这个 WindowToken 是隐式创建的

       接下来的代码逻辑就是 WindowToken 不为 null 的情况,根据 rootType 和 type 的值进行判断,比如在注释8处判断如果窗口为应用程序窗口,在注释9处将 WindowToken 转换为专门针对应用程序窗口的 AppWindowToken ,然后根据 AppWindowToken 的值进行后续的判断。

       在注释10处创建了 WindowState ,它存有窗口的所有的状态信息,在 WMS 中它代表一个窗口。在创建 WindowState 传入的参数中, this 指的是 WMS,client 指的是IWindow,IWindow 会将 WMS 中窗口管理的操作回调给 ViewRootlmpl,,token 指的是 WindowToken ,

       在注释11和注释12处分别判断请求添加窗口的客户端是否已经死亡、 窗口的 DisplayContent 是否 null ,如果是则不再执行下面的代码逻辑。

       在注释13处调用了 WMP 的 adjustWindowParamsLw 方法, 改方法PhoneWindowManager 中实现,此方法会根据窗口的 type 对窗口的 LayoutParams 的一些成员变量进行修改。

       在注释14处调用 WMP 的 prepareAddWindowLw 方法,用于准备将窗口添加到系统中。

       在注释15处将 WindowState 添加到 mWindowMap 中。

       在注释16处将 WindowState 添加到该 WindowState 对应的 WindowToken 中(实际是保存在 WindowToken 的父类 WindowContainer 中),这样 WindowToken 就包含了同一个组件的 WindowState。

       addWindow方法总结:

       (1) 对所要添加的窗口进行检查,如过窗口不满足一些条件,就不再执行下面的代码逻辑。

       (2) WindowToken 相关的处理,比如有的类型需要提供 WindowToken ,没有提供的话就不执行下面的代码逻辑,有的窗口类型需要由 WMS 隐式创建 WindowToken。

       (3) WindowState 的创建和相关处理,将 WindowToken WindowState 相关联。

       (4) 创建和配置 DisplayContent,完成窗口添加到系统前的准备工作。

 

    4. Window的删除过程

       Window的删除过程与Window的创建和更新过程一样,要删除Window需要先调WindowManagerImpl 的 removeView 方法,在 removeView 方法中又会调用 WindowManagerGlobal 的 removeView 方法,然后就从这里开始分析起。WindowManagerGlobal 的 removeView 方法如下所示:

frameworks/base/core/java/android/view/WindowManagerGlobal.java
    public void removeView(View view, boolean immediate) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }

        synchronized (mLock) {
            int index = findViewLocked(view, true); // ... 1
            View curView = mRoots.get(index).getView();
            removeViewLocked(index, immediate); // ... 2
            if (curView == view) {
                return;
            }

            throw new IllegalStateException("Calling with view " + view
                    + " but the ViewAncestor is attached to " + curView);
        }
    }
       在注释1 处找到要删除的Window(View)在 Vi ew 列 表中的索引,在注释2 处调用了 re move View Locked 方法 并将这个索引传进去,如下所示:
 

frameworks/base/core/java/android/view/WindowManagerGlobal.java

    private void removeViewLocked(int index, boolean immediate) {
        ViewRootImpl root = mRoots.get(index); // ... 1
        View view = root.getView();

        if (view != null) {
            InputMethodManager imm = InputMethodManager.getInstance(); // ... 2
            if (imm != null) {
                imm.windowDismissed(mViews.get(index).getWindowToken());// ... 3
            }
        }
        boolean deferred = root.die(immediate); // ... 4
        if (view != null) {
            view.assignParent(null);
            if (deferred) {
                mDyingViews.add(view);
            }
        }
    }

       在注释1处根据传入的索引在 ViewRootlmpl 列表中获得 ViewRootlmpl 。在注释 2 处得到 InputMethodManager 实例,如 InputMethodManager 实例不为 null ,则在注释 3 处调用 InputMethodManager 的 windowDismissed 方法来结束要删除Window(View)的输入法相关的逻辑。在注释4处调用 ViewRootlmpl 的 die 方法,如下所示:

frameworks/base/core/java/android/view/ViewRootlmpl.java

    boolean die(boolean immediate) {
        // die方法需要立即执行并且此时ViewRootImpl不再执行performTraversals方法
        if (immediate && !mIsInTraversal) { // ... 1
            doDie(); // ... 2
            return false;
        }

        if (!mIsDrawing) {
            destroyHardwareRenderer();
        } else {
            Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
                    "  window=" + this + ", title=" + mWindowAttributes.getTitle());
        }
        mHandler.sendEmptyMessage(MSG_DIE);
        return true;
    }
       在注释1  处如果 immediate 为 ture   (需要立 即执行),并且 mIsInTraversal 值为 false 则执行注释 2 处的代码, mIsInTraversal 在执行 ViewRootlmpl 的  perform Traversals 方法时 会被 true ,在 perform Traversals 方法 执行完时被设置为 false ,因此注释1 处可以理解为 die 方法需要立即执行并且此时 ViewRootlmpl 不再执行 perfo rm Traversals 方法。   在注释 2 处的 doDie 方法如下所示:
 

frameworks/base/core/java/android/view/ViewRootlmpl.java

    void doDie() {
        // 检查执行doDie方法的线程的正确性
        checkThread(); // ... 1
        if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
        synchronized (this) {
            if (mRemoved) { // ... 2
                return;
            }
            mRemoved = true; // ... 3
            if (mAdded) { // ... 4
                dispatchDetachedFromWindow(); // ... 5
            }
 
            if (mAdded && !mFirst) { // ... 6
                destroyHardwareRenderer();

                if (mView != null) {
                    int viewVisibility = mView.getVisibility();
                    boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
                    if (mWindowAttributesChanged || viewVisibilityChanged) {
                        // If layout params have been changed, first give them
                        // to the window manager to make sure it has the correct
                        // animation info.
                        try {
                            if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
                                    & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                                mWindowSession.finishDrawing(mWindow);
                            }
                        } catch (RemoteException e) {
                        }
                    }

                    mSurface.release();
                }
            }

            mAdded = false;
        }
        WindowManagerGlobal.getInstance().doRemoveView(this); // ... 7
    }
       在注释1 处用于检查执行 doDie 方法的线程的正确性,其 内部会判断执行 doDie 方法的线程是否是创建要删除Window(View) 的原始线程, 如果不是 就会抛出异常,这是 因为 只有创建 要删除Window(View) 的原始线程才能够操作 。注释2 到注释3 处的代码用于防止 do Die 方法 被重复调用。在注释4处要删除的Window(View)  有子 View 就会调用 注释5 处的 dispatchDetachedFrom Window 方法来销毁 View 。在注释6处如果 要删除的Window(View) 有子 View 并且不是第一 次被添加,就会执行后面的代码逻辑。在注释7 处的 WindowManagerGlobal 的  doRemove View 方法   ,如下所示:
 
frameworks/base/core/java/android/view/WindowManagerGlobal.java
 
    void doRemoveView(ViewRootImpl root) {
        synchronized (mLock) {
            final int index = mRoots.indexOf(root); // ... 1
            if (index >= 0) {
                mRoots.remove(index);
                mParams.remove(index);
                final View view = mViews.remove(index);
                mDyingViews.remove(view);
            }
        }
        if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
            doTrimForeground();
        }
    }
       在 WindowManagerGlobal 中维护了和 Window 操作相关的三个列 表, doRemove View 方法 会从这三 个列表中清除要删除Window(View) 对应的元素。在注释 1 处找到要删除Window(View)对应的 ViewRootimpl 在 ViewRootimpl 列表中的索引,接着根据这个索引从 ViewRootlmpl 列表、 布局参数列表和 View 列表中删除与要删除Window(View)对应的元素 。我 们接着回到 ViewRootlmpl 的  doDie 方法,查看注释5处的 dispatchDetachedFrom Window 方法,代码如下所示
 
frameworks/base/core/java/android/view/ViewRootlmpl.java
 
    void dispatchDetachedFromWindow() {
        ...
        try {
            mWindowSession.remove(mWindow);
        } catch (RemoteException e) {
        }
         ...
    }
       在dispatchDetachedFrom Window 方法 主要 调用了 IWindowSession 的  remove 方法 , IWindowSession在 Serv er 端的实 现为 Session 。 Session 的  remove 方法如下所示:
 
frameworks/base/services/core/java/com/android/server/wm/Session.java
    public void remove(IWindow window) {
        mService.removeWindow(this, window);
    }
       接着查看 WMS 的  remove Window 方法,代码如下所示
 
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
 
    void removeWindow(Session session, IWindow client) {
        synchronized(mWindowMap) {
            WindowState win = windowForClientLocked(session, client, false); // ... 1
            if (win == null) {
                return;
            }
            win.removeIfPossible(); // ... 2
        }
    }
       在注释1 处用于获取 Window 对应的 WindowState, WindowState 用于保存窗口的信息, 在WMS 中它用来描述一个窗口  。接着 在注释2 处调用 WindowState 的  removelfPossible 方法,如下所示:
 
frameworks/base/services/core/java/com/android/service/wm/WindowState.java
    void removeIfPossible() {
        super.removeIfPossible();
        removeIfPossible(false /*keepVisibleDeadWindow*/);
    }


    private void removeIfPossible(boolean keepVisibleDeadWindow) {
        ...
        removeImmediately(); // ... 1
        // Removing a visible window will effect the computed orientation
        // So just update orientation if needed.
        if (wasVisible && mService.updateOrientationFromAppTokensLocked(false, displayId)) {
            mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
        }
        mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
        Binder.restoreCallingIdentity(origId);
    }
       removelfPossible 方法并不是直接执行删除操作的, 而是进行多个条 件判断过滤,满足其中一个条件就 return  ,推迟删除操作。比如要删除的Window(View) 正在运行一 个动画, 这时就得推迟删除操作,直到动画完成。  通过这些条件判断过滤就会执行注释1 处的 remo velmmedi ately 方法,代码如下所示
 
frameworks/base/services/core/java/com/android/service/wm/WindowState.java
 
  @Override
    void removeImmediately() {
        super.removeImmediately();

        if (mRemoved) { // ... 1
            // Nothing to do.
            if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
                    "WS.removeImmediately: " + this + " Already removed...");
            return;
        }

        mRemoved = true; // ... 2

        ...
        mPolicy.removeWindowLw(this); // ... 3

        disposeInputChannel();

        mWinAnimator.destroyDeferredSurfaceLocked();
        mWinAnimator.destroySurfaceLocked();
        mSession.windowRemovedLocked(); // ... 4
        try {
            mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
        } catch (RuntimeException e) {
            // Ignore if it has already been removed (usually because
            // we are doing this as part of processing a death note.)
        }

        mService.postWindowRemoveCleanupLocked(this); // ... 5
    }
       removelmmediately 方法 用于立 即进行删除操作 。在注释1 处的 mRemoved 为 true 表示 正在执行删除 Window 操作,在注释 1 到注释2 处之间的代码用 于防止重复删除操作。在注释3处如果当前要删除的 Window 是  StatusBar 或者 N avigationBar 就会将这个 Window 从对应的控制器中删除 。在注释4 处将要删除的Window 对应的 Session 从  WMS 的 ArraySet < Session > mSessions 中删除并清除 Session 对应的 SurfaceSession 资源 ( SurfaceSession 是  SurfaceFlinger 的一 个连接,通过这个连接可以创建一 个或者多个 Surface 并 渲染到屏幕上)。在注释5处调用了 WMS 的  postWindowRemoveCleanupLocked 方法 用于对要删除的Window 进行一些集中的清理工作
 
      Window删除过程大概如下:
       1. 检查删除线程的正确性,如果不正确就会抛出异常。
       2. 从ViewRootImpl列表、布局参数列表和View列表中删除与要删除的Window对应的元素。
       3. 判断是否可以立即执行删除操作,如果不可以就推迟删除操作。
       4. 执行删除操作,清理和释放与要删除的Window相关的一些资源。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值