基础概念

Window

在 Android 体系中,屏幕的笼统是 DisplayContent ,在屏幕的笼统上,经过不同的窗口,展现不同的应用程序页面和一些其他UI 组件(例如 Dialog、状况栏等)。 Window 经过在 Z 轴叠放,从而完结了杂乱的交互逻辑。

Window 是一个处理尖端窗口外观和行为战略的笼统基类,它的详细完结是 PhoneWindow 类,PhoneWindow 对 View 进行办理。

如用户所见,Android 应用程序中的页面以 Activity 组件的方式呈现,而在 Activity 初始化时,就会创立 PhoneWindow ,这样 Activity 组件就具有了窗口作为容器来展现各种各样的 View 与用户进行交互。

Window 是尖端窗口外观和行为战略的笼统基类。它的实例应用作增加到 Window Manager 的尖端视图。 它供给了标准的 UI 战略,例如背景、标题区域、默许按键处理等。

此笼统类的仅有现有完结是 android.view.PhoneWindow,您应该在需求 Window 时对其进行实例化。

提前需求了解的是 Window 的办理需求与体系的服务进行通讯 ,这是一套 IPC 机制,CS 架构,服务端是 WindowManagerService,客户端是 WindowManager 。

PhoneWindow 的创立

在 Activity 发动阶段,最终调用的 ActivityThread 的 performLaunchActivity 办法中,调用到了 Activity 的 attach 办法,PhoneWindow 便是在此时创立的:

Android Window 管理机制

而且它还设置了一个 WindowManager:

Android Window 管理机制

这个办法在 Window 中完结,假如传入的 WindowManager 为 null,内部获取了一个 WindowManagerImpl 来作为 WindowManager 。WindowManager 是体系供给的窗口办理东西,与 Android 的窗口和 UI 展现息息相关,后续会详细解读。

WindowType 和显现次第

Android 中的关于窗口的概念有很多,比方应用程序页面、体系过错弹窗、输入法窗口、PopupWindow 、 Toast 、 Dialog 等等。总的来说分为三大类:

  1. Application Window:应用程序窗口,Activity 便是一个典型的应用程序窗口,它的 Type 枚举范围在 1 – 99 。这个数值会触及 窗口的层级。
  2. System Window:体系窗口,Toast 、输入法窗口、体系音量窗口、体系过错窗口都属于体系窗口。范围在 2000 – 2999 。
  3. Sub Window:子窗口,不能独立存在,需求依附于其他窗口,典型的比如是 PopupWindow 。它的 Type 范围是 1000 -1999 。

窗口在代码中经过 Window 接口表明,而窗口类型,便是经过 Window 的 type 特点表明,它被界说在 WindowManager 的内部类 LayoutParams 中:

@WindowType
public int type;
@IntDef(prefix = "TYPE_", value = {
    TYPE_BASE_APPLICATION,
    TYPE_APPLICATION,
    TYPE_APPLICATION_STARTING,
  	//....
})
@Retention(RetentionPolicy.SOURCE)
public @interface WindowType {}
窗口的显现次第

当一个进程向 WMS 恳求一个窗口时,WMS 会为窗口确认显现次第。为了便利次第的办理,手机能够虚拟地用 X 、 Y 、 Z 轴来表明, Z 轴垂直于屏幕,由 Z 轴来确认窗口的显现次第。一般状况下,WindowType 值越大,显现层级越高,即越挨近用户,不容易被遮挡。当然也有比较杂乱的规矩,这儿仅仅阐明基础层级规矩。

Window Flag

同样在 WindowManager 的内部类 LayoutParams 中界说。用来操控 Window 的显现和行为。

        /** Window flag: as long as this window is visible to the user, allow
         *  the lock screen to activate while the screen is on.
         *  This can be used independently, or in combination with
         *  {@link #FLAG_KEEP_SCREEN_ON} and/or {@link #FLAG_SHOW_WHEN_LOCKED} */
        public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON     = 0x00000001;
        /** Window flag: everything behind this window will be dimmed.
         *  Use {@link #dimAmount} to control the amount of dim. */
        public static final int FLAG_DIM_BEHIND        = 0x00000002;
        /** Window flag: enable blur behind for this window. */
        public static final int FLAG_BLUR_BEHIND        = 0x00000004;
				// ...

例如常见的例如 FLAG_BOT_TOUCHABLLE ,表明窗口不在接纳任何触摸事情。

为 Window 增加 Flag 能够经过 addFlags 办法和 setFlags 办法:

window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
// or
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

也能够经过 WindowManager.LayoutParams 去设置:

val lp = WindowManager.LayoutParams()
lp.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN
val tv = TextView(context)
windowManager.addView(tv, lp)

Window SoftInputMode

SoftInputMode 是用来操控软键盘相关的。窗口叠加是很常见的场景,尤其是在输入的时分,默许状况下输入发会顶在输入框下方,输入框下方的内容遮盖住,这种场景在例如登录界面是十分不好的体会。为了能够自在的操控软键盘窗口,WindowManager 的 LayoutParams 供给了软键盘相关的模式:

Android Window 管理机制

截图中仅为部分特点,这些内容能够在 AndroidManifest.xml 中的 Activity 的特点:android:windowSoftInputMode 设置。当然也能够经过代码去进行设置:

window.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST

WindowManager

Window 由窗口办理器 WindowManager 来进行办理,它经过与 WindowManagerService 进行通讯,作为客户端的东西类来办理 Window :

@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager

ViewManager 中界说了对 View 办理,他有三个办法,别离代表了增加、更新和删去:

  • 增加:addView
  • 更新:updateViewLayout
  • 删去:removeView

WindowManager 因为承继联系,所以具有了办理 View 的才能,事实上,WindowManager 实践办理的是 Window 中的 View ,并经过 Binder 机制来与 WindowManagerService(WMS)进行 IPC 通讯,经过 WMS 来办理 Window 。

WindowManager 的完结是 WindowManagerImpl ,但它的内部并没有完结逻辑,而是经过桥接模式,将功用托付给 WindowManagerGlobal 来完结。

WindowManagerGlobal 是一个单例,一个进程只要一个 WindowManagerGlobal 实例。担任 WindowManager 的详细功用完结。WindowManagerGlobal 中包含了 View 的增加、删去和更新的核心代码。

WindowManagerGlobal 中处理 View 的增加、更新和删去,与 ViewRootImpl 密切相关。

ViewRootImpl

ViewRootImpl 代表的是 Android 视图层次结构的顶部,是 View 和 WindowManager 之间的桥梁。Android 的视图层次结构是树结构,ViewRootImpl 实践上便是树的根节点的笼统。WindowManager 经过根节点目标,来更新整个树结构上的 View 节点的内容。

一个 Window 会有一个根节点,这一点我们能够经过 Android 的视图结构亦或是无障碍服务节点的树结构来证明。所以,在客户端 WindowManager 这一侧,实践上是在更新树结构;而在服务端 WMS 中的逻辑,则是办理窗口与屏幕之间的联系。

WindowManagerService

WindowManagerService 是体系中重要的服务,WindowManagerService 担任办理窗口的发动、增加、删去、大小和层级等;它还持有 InputManagerService 的引证来处理窗口的触摸事情;亦或是处理窗口之间切换动画。

注意:本文只解说 WMS 处理窗口相关的内容。

WMS 创立流程

简述创立流程,有爱好可自行参考源码。

WMS 是在 SystemServer 进行中创立的, SystemServer 履行到 startOtherServices 办法中发动 WMS 。在 startOtherServices 办法中创立流程是:

  • 初始化 Watchdog ,这是一个监控体系要害服务的运转状况的监控器。
  • 创立 InputManagerService 。
  • 开端创立 WMS, 运转 WMS 的 main 办法,将 IMS 传递给 WMS (WMS 持有 IMS 引证)。
    • 经过 DisplayManager 获取 Display 数组。
    • 将 Display 封装成 DisplayContent ,用来作为屏幕的笼统。
    • 获取 AMS 的实例(WMS 持有 AMS 的引证)。
    • 创立 WindowAnimator 。
    • 初始化 WindowManagerPolicy 。
    • 将 WMS 增加到 Watchdog 中。
  • WMS、IMS 注册到 ServiceManager 中。
  • 初始化屏幕信息,调用 WMS 的 displayReady 办法。
  • 经过调研 WMS 的 systemReady 办法告诉 WMS 初始化完结。

Session

Session 是用于其他应用程序和 WMS 通讯之间的通道,每个应用程序都会有一个 Session ,在 WMS 经过 mSessions 特点保存,它的类型是 ArraySet<Session>

WindowContainer

WindowContainer 界说了一组能够直接或经过其子类以层次结构方式保存 Window 的类的常用功用。

WindowState

WindowState 代表 WMS 办理的一个窗口,承继自 WindowContainer<WindowState> ,并完结了一些其他接口。

WindowToken

WindowToken 表明 WMS 中一组 Window 的容器。通常这是表明一个应用程序中用于显现一组窗口 Activity ;对于嵌套的窗口组合来说,则需求为其父窗口创立一个 WindowToken 来办理子窗口。

Window 增加进程

WindowManager 增加进程

窗口的增加进程进口是 WindowManager 的 addView 办法,实践逻辑在 WindowManagerGlobal 中:

    public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow, int userId) {
        // ...
        ViewRootImpl root;
        View panelParentView = null;
        synchronized (mLock) {
            // ...
            if (windowlessSession == null) {
                root = new ViewRootImpl(view.getContext(), display);
            } else {
                root = new ViewRootImpl(view.getContext(), display,
                        windowlessSession);
            }
            view.setLayoutParams(wparams);
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
            try {
                root.setView(view, wparams, panelParentView, userId);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

这儿只提取要害的逻辑:

  1. 依据 windowlessSession (IWindowSession)是否可空创立 ViewRootImpl
  2. 给 view 设置布局参数
  3. 将 View 、ViewRootImpl 、LayoutParams 别离保存到各自的数组中
  4. 最终调用 ViewRootImpl 的 setView 办法

处理逻辑从 WindowManagerGlobal 来到了 ViewRootImpl 中,持续跟进 setView 办法:

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) {
        synchronized (this) {
            if (mView == null) {
              	mView = view;
                // ...
                int res; /* = WindowManagerImpl.ADD_OKAY; */
                // 在增加到窗口办理器之前组织第一次布局,以保证我们在从体系接纳任何其他事情之前进行布局。
                requestLayout();
                // ...
                try {
                    // ... 
                    res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), userId,
                            mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
                            mTempControls);
                    // ... 处理窗口转化
                } catch (RemoteException e) {
                    // ... 
                } finally {
                    // ...
                }
                //  WindowLayout 用于核算窗口框架尺度
                mWindowLayout.computeFrames(mWindowAttributes, state,
                        displayCutoutSafe, winConfig.getBounds(), winConfig.getWindowingMode(),
                        UNSPECIFIED_LENGTH, UNSPECIFIED_LENGTH,
                        mInsetsController.getRequestedVisibilities(),
                        getAttachedWindowFrame(), 1f /* compactScale */, mTmpFrames);
                setFrame(mTmpFrames.frame);
                // ...
            }
        }
    }

这部分代码只截取要害逻辑:

  1. 在真实增加到 WMS 之前,组织了一次布局,以保证我们在从体系接纳任何其他事情之前现已进行布局
  2. 经过 mWindowSession.addToDisplayAsUser 调用到 WMS 中的逻辑
  3. 最终拿到 WMS 增加窗口的处理成果后,从头核算并更新窗口的 Frame

在第二步中, mWindowSession 的类型是 IWindowSession.aidl ,它的完结是 Session :

class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
		final WindowManagerService mService;
		// ...
		@Override
    public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities,
            InputChannel outInputChannel, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls) {
        return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
                requestedVisibilities, outInputChannel, outInsetsState, outActiveControls);
    }
  	// ...
}

Session 自身便是 Binder ,经过 Session ,跨进程调用到了 WMS 的 addWindow 办法,此时流程进入到体系服务端。这个流程在后续 WMS 的流程中剖析,这儿能够看出 addToDisplayAsUser 回来了 WMS 的处理成果,经过 int 方式回来。

依据 WMS 的处理成果 res ,WindowManager 中,持续核算了 Frame ,经过核算后经过 ViewRootImpl 的 setFrame(Rect) 办法进行设置:

private void setFrame(Rect frame) {
    mWinFrame.set(frame); // mWinFrame: Rect
    // ...
    mInsetsController.onFrameChanged(mOverrideInsetsFrame != null ?
            mOverrideInsetsFrame : frame);
}

mInsetsController 的类型是 InsetsController ,它是 WindowInsetsController 在客户端的完结,用来操控出产嵌入窗口的接口。也便是说这儿会处理嵌入窗口结构的尺度更新:

    // in InsetsController.java
		@VisibleForTesting
    public void onFrameChanged(Rect frame) {
        if (mFrame.equals(frame)) {
            return;
        }
        mHost.notifyInsetsChanged();
        mFrame.set(frame);
    }

这儿调用 mHost 告诉更新,mHost 是 InsetsController.Host 接口目标,这个接口有两个完结,在 ViewRootImpl 的创立是:

mInsetsController = new InsetsController(new ViewRootInsetsControllerHost(this));

ViewRootInsetsControllerHost 中的 notifyInsetsChanged 办法:

    @Override
    public void notifyInsetsChanged() {
        mViewRoot.notifyInsetsChanged();
    }

调回到了 ViewRootImpl 自己的办法:

    void notifyInsetsChanged() {
        mApplyInsetsRequested = true;
        requestLayout();
        // See comment for View.sForceLayoutWhenInsetsChanged
        if (View.sForceLayoutWhenInsetsChanged && mView != null && (mWindowAttributes.softInputMode & SOFT_INPUT_MASK_ADJUST) == SOFT_INPUT_ADJUST_RESIZE) {
            forceLayout(mView);
        }
        // If this changes during traversal, no need to schedule another one as it will dispatch it during the current traversal.
        if (!mIsInTraversal) {
            scheduleTraversals();
        }
    }

在这儿恳求进行布局:

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

查看线程,然后履行 scheduleTraversals :

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

scheduleTraversals 办法中经过 mChoreographer 目标发布一个回调来运转下一帧。Choreographer 是用来和谐动画,输入和绘图的时刻的类。履行的 Runnable 类型是 TraversalRunnable :

    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

doTraversal 办法中,触发了 View 的制作流程的核心办法 performTraversals :

    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            performTraversals();
        }
    }

performTraversals 办法使得窗口中的视图树开端 View 的作业流程: 1. relayoutWindow,内部调用 IWindowSession 的 relayout 办法来更新 Window视图。 2. performMeasure,内部调用 View 的 measure 。 3. performLayout,内部调用 View 的 layout 。 4. performDraw,内部调用 View 的 draw 。

这样就完结了对 Window 中 UI 的改写。

WindowManagerService 增加进程

在 ViewRootImpl 的 setView 办法中,经过 Session 调用到了 WMS 的 addWindow 办法,addWindow 中逻辑十分杂乱:

  1. 经过 WindowManagerPolicy 的 checkAddPermission 查看增加权限

  2. 经过 displayId 获取所增加到的屏幕笼统 DisplayContent 目标

  3. 依据 Window 是否是子窗口,创立 WindowToken

  4. 假如上一步创立 token 失败,从头依据是否存在父窗口创立 WindowToken

    1. 子窗口直接取父窗口的 WindowToken
    2. 新的窗口直接构造一个新的 WindowToken
  5. 依据根窗口的 type 来处理不同的状况(Toast、键盘、无障碍浮层等不同类型回来不同的成果)

  6. 创立 WindowState

  7. 查看客户端状况,判别是否能够将 Window 增加到体系中

  8. 以 session 为 key ,windowState 为 value ,存入到 mWindowMap 中:

        /** Mapping from an IWindow IBinder to the server's Window object. */
        final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>();
    
  9. 将 WindowState 增加到相应的 WindowToken 中

    1. 若 WindowState 代表一个子窗口,直接 return

    2. 若仍没有 SurfaceControl ,为该 token 创立 Surface

    3. 更新 Layer

    4. 依据 Z 轴排序顺序将 WindowState 所代表的 Window 增加到合适的方位,此数据结构保存在 WindowToken 的父类 WindowContainer 的 mChildren 中:

      protected final WindowList<E> mChildren = new WindowList<E>();
      class WindowList<E> extends ArrayList<E> {
          void addFirst(E e) {
              add(0, e);
          }
          E peekLast() {
              return size() > 0 ? get(size() - 1) : null;
          }
          E peekFirst() {
              return size() > 0 ? get(0) : null;
          }
      }
      
  10. 查看是否需求转化动画

  11. 更新窗口焦点

  12. 最终回来履行成果

在 ViewRootImpl 中的 setView 办法拿到成果后,开端从头核算尺度,并从头制作。

Window 更新进程

Window 的更新进程与 Window 的增加进程类似,经过 updateViewLayout 办法,它的完结也是在 WindowManagerImpl 中,同样的,实践调用的是 WindowManagerGlobal 对应的办法 updateViewLayout:

    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
        view.setLayoutParams(wparams);
        synchronized (mLock) {
            int index = findViewLocked(view, true);
            ViewRootImpl root = mRoots.get(index);
            mParams.remove(index);
            mParams.add(index, wparams);
            root.setLayoutParams(wparams, false);
        }
    }

更新进程没有触及 WMS ,直接给参数 view 更新 LayoutParams ,然后同步更新了 ViewRootImpl 维护的 root / view / params 数组中的值。

在 ViewRootImpl 的 setLayoutParams 中,会调用 requestLayout 从头制作 UI 。

Window 删去进程

Window 的删去进程经过 WindowManager 的 removeView 办法进行的,实践逻辑在 WindowManagerGlobal 的完结中:

    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(); // 2 
            removeViewLocked(index, immediate); // 3
            if (curView == view) {
                return;
            }
            throw new IllegalStateException("Calling with view " + view
                    + " but the ViewAncestor is attached to " + curView);
        }
    }

注释 1 处首要经过 view 取查找 View 地点的索引。注释 2 从 mRoots 中依据索引找出 ViewRootImpl ,经过 ViewRootImpl 找到它的 View ,最终在注释 3 处调用 removeViewLocked 办法进行删去:

		// WindowManagerGlobal.removeViewLocked
		private void removeViewLocked(int index, boolean immediate) {
        ViewRootImpl root = mRoots.get(index);
        View view = root.getView();
        if (root != null) {
            root.getImeFocusController().onWindowDismissed(); // 1
        }
        boolean deferred = root.die(immediate); // 2
        if (view != null) {
            view.assignParent(null);
            if (deferred) {
                mDyingViews.add(view);
            }
        }
    }

在这个办法中,首要也是获取 ViewRootImpl 和 View 目标,假如 ViewRootImpl 存在,经过注释 1 处的调用去铲除输入焦点,结束处理输入法相关的逻辑。然后在注释 2 处调用 ViewRootImpl 目标的 die 办法:

// ViewRootImpl.die
boolean die(boolean immediate) {
    if (immediate && !mIsInTraversal) {
        doDie(); // 1
        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;
}

die 办法中,在需求立即履行且当前没有进行 Traversal 的状况下履行 doDie 办法:

void doDie() {
    checkThread(); // 1
    synchronized (this) {
        // 防止屡次履行 doDie
        if (mRemoved) {
            return;
        }
        mRemoved = true;
        if (mAdded) {
            dispatchDetachedFromWindow(); // 2
        }
        // 子 View 且不是初次增加的状况下,履行这些逻辑
        if (mAdded && !mFirst) {
            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, null /* postDrawTransaction */);
                        }
                    } catch (RemoteException e) {
                    }
                }
                destroySurface(); 
            }
        }
        mAdded = false;
    }
    WindowManagerGlobal.getInstance().doRemoveView(this); // 3
}

在 doDie 办法中,首要进行的便是线程查看,然后保证同步的状况下,假如存在子 View , 调用注释 2 处的 dispatchDetachedFromWindow 办法;然后处理子 View 且不是初次增加的状况,最终调用注释 3 处的 WindowManagerGlobal 的 doRemoveView 办法。

首要来看 dispatchDetachedFromWindow :

void dispatchDetachedFromWindow() {
    // ...
    // 视图树分发 dispatchDetachedFromWindow
    if (mView != null && mView.mAttachInfo != null) {
        mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
        mView.dispatchDetachedFromWindow();
    }
    // 毁掉硬件烘托
    destroyHardwareRenderer();
    // 铲除无障碍焦点
    setAccessibilityFocus(null, null);
    // 撤销动画
    mInsetsController.cancelExistingAnimations();
    // 开释资源
    mView.assignParent(null);
    mView = null;
    mAttachInfo.mRootView = null;
    // 毁掉 Surface
    destroySurface(); // 1
    // ...
    try {
        mWindowSession.remove(mWindow); // 2
    } catch (RemoteException e) {
    }
    // ... 
    // 删去待履行的 Traversals
    unscheduleTraversals();
}

dispatchDetachedFromWindow 办法中,开端向视图树中的 View 分发 DetachedFromWindow ,然后开释一些资源,在注释 1 处开释 Surface ,注释 2 处经过 Session IPC 调用了 WMS 移除 Window :

		// Session.remove
		@Override
    public void remove(IWindow window) {
        mService.removeWindow(this, window);
    }

此时来到了服务端 WMS 的 removeWindow 办法中:

		// WMS .removeWindow
		void removeWindow(Session session, IWindow client) {
        synchronized (mGlobalLock) {
            WindowState win = windowForClientLocked(session, client, false);
            if (win != null) {
                win.removeIfPossible();
                return;
            }
            // Remove embedded window map if the token belongs to an embedded window
            mEmbeddedWindowController.remove(client);
        }
    }

经过 windowForClientLocked 办法获取到了待移除 Window 的描述目标 WindowState ,调用它的 removeIfPossible 办法:

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

内部调用了同名不同参的办法:

// WindowState.removeIfPossible
private void removeIfPossible(boolean keepVisibleDeadWindow) {
    try {
        // ... 条件判别过滤,满足条件会直接 return 推迟删去操作
        removeImmediately(); // 1
        // 删去一个可见窗口将影响核算方向,假如需求,更新方向,
        if (wasVisible) {
            final DisplayContent displayContent = getDisplayContent();
            if (displayContent.updateOrientation()) {
                displayContent.sendNewConfiguration();
            }
        }
        // 2
        mWmService.updateFocusedWindowLocked(isFocused() 
                        ? UPDATE_FOCUS_REMOVING_FOCUS
                        : UPDATE_FOCUS_NORMAL,
                true /*updateInputWindows*/);
    } finally {
        Binder.restoreCallingIdentity(origId);
    }
}

在这个办法中,首要进行一些条件过滤,然后调用注释 1 处的 removeImmediately 履行删去操作,然后核算方向,注释 2 处更新 WMS 的 Window 焦点。跟进 removeImmediately 办法:

@Override
void removeImmediately() {
    // 保证同一时刻只履行一次 removeImmediately
    if (mRemoved) {
        return;
    }
    mRemoved = true;
    // ...
    mSession.windowRemovedLocked(); // 1 
    try {
        mClient.asBinder().unlinkToDeath(mDeathRecipient, 0); // 2 免除与客户端的衔接
    } catch (RuntimeException e) {
        // Ignore if it has already been removed (usually because
        // we are doing this as part of processing a death note.)
    }
    mWmService.postWindowRemoveCleanupLocked(this); // 3 履行一些会集清理的作业
}

在 removeImmediately 办法中:

  • 注释 1 处处理 Session 相关的逻辑,从 mSessions 中删去了当前 mSession ,并铲除 mSession 对应的 SurfaceSession 资源,(SurfaceSession 是 SurfaceFlinger 的一个衔接,经过这个衔接能够创立多个 Surface 并烘托到屏幕上);
  • 注释 2 处免除与客户端的衔接;
  • 注释 3 处会集履行了一些清理作业。

总结

Window 的办理是根据 C/S 架构,依靠体系服务。服务端是 WMS ,客户端是 WindowManager 。

Window 的更新进程不触及 WMS ,而增加和删去需求服务端与客户端一同合作,WindowManager 来恳求履行,并处理 UI 烘托改写,WMS 则是担任办理 Window 与体系其他才能结合。

Window 的概念不同于 View ,尽管看起来十分相似,但它们是两种概念。

  • Window 代表一套视图树的容器,而 View 是视图树中的节点。
  • ViewRootImpl 是视图树目标。
  • WindowState 用来描述一个 Window。
  • WindowToken 用来表明一组 Window 的容器,能够代表 Activity 和嵌套窗口的父容器。

别的,不同类型的 Window 增加、删去的逻辑略有不同,本文并没有介绍它们的差异性,感爱好的读者能够自行查看源码。