前言

我们知道,在 Windows 操作系统中,每一项任务都是在一个打开的窗口中进行的,窗口的概念非常好理解。而在 Android 中,其实窗口也是一个非常重要的概念,但是却很少被我们接触。
其实,和 Windows 操作系统一样,Android 中的每一个视图,例如一个 Activity、一个 Dialog 或者一个 Toast,它们都是一个窗口,这些窗口来自不同的进程,却全部由一个系统服务(WindowManagerService)统一管理。
我们也可以直接利用 WindowManager 来控制自己的 Window。

Window 的添加过程

WindowManager 有三个方法:

public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);

这也是 WindowManager 提供的所有给开发者的方法,可以看到,这些方法都是直接操作 View 本身,而完全看不到 Window 的踪迹。其实,Window 是一个抽象的概念,具体的显示仍然是 View 的工作。
任何视图的显示都是通过 View,而任何 View 必须是依附于一个 Window,才能被显示。
一个 Window 包含着一个 View 和一个 ViewRootImpl,View 即需要被显示的 View 层次的根 View,而 ViewRootImpl 负责 Window 和 View 之间的沟通。

Window 被添加需要通过 WindowManager 的 addView 方法,而 WindowManager 是一个接口类,addView 真正的实现是在 WindowManagerImpl 中:

@Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        mGlobal.updateViewLayout(view, params);
    }

        @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }

可以看到,WindowManagerImpl 又将所有操作委托给了 WindowManagerGlobal 来实现,WindowManagerGlobal 的 addView主要分为几个步骤:

1. 检查参数是否合法,如果 Window 类型是 SubWindow(1000-1999),则还需要调整一些参数

if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        }

2. 创建 ViewRootImpl 并将 View 添加到列表中

因为 WindowManagerGlobal 管理的是整个系统的 Window,在 WindowManagerGlobal 中,维护着几个列表:

private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
    private final ArraySet<View> mDyingViews = new ArraySet<View>();

其中,mView 对应着所有 Window 中的 View 层次的根 View,mRoots 对应着所有 Window 中的 ViewRootImpl,mParams 对应着所有 Window 中的布局参数,而 mDyingViews 保存了那些已经调用了 removeView 但还没删除的 View。
在 addView 接下来的步骤中,将要被添加的 Window 的 ViewRootImpl 被创建。

ViewRootImpl root;
     root = new ViewRootImpl(view.getContext(), display);
     view.setLayoutParams(wparams);
     mViews.add(view);
     mRoots.add(root);
     mParams.add(wparams);

3. 通过 ViewRootImpl 来更新界面,并完成 Window 的添加

addView 方法的最后,调用 ViewRootImpl 的 setView 方法

root.setView(view, wparams, panelParentView);

而在 setView 方法中,首先调用了 requestLayout() 方法

// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
@Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

这里的 scheduleTraversals 方法,其实就是 View 层级的整个测量、布局和绘制迭代过程的入口,也就是说,到这里此 Window 中的 View 层级的绘制过程已经开始。

在 setView 方法的最后,会通过 WindowSession 的 addToDisplay 方法,执行添加 Window 的最后一步:

try {
       mOrigWindowType = mWindowAttributes.type;
       mAttachInfo.mRecomputeGlobalAttributes = true;
       collectViewAttributes();
       res = mWindowSession.addToDisplay(
                mSeq, 
                mWindowAttributes,
                getHostVisibility(),
                mDisplay.getDisplayId(),
                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                mAttachInfo.mOutsets,
                mInputChannel
       );
}
@Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) {

        // 最终将 Window 添加请求交给 WindowManagerService 处理了
        return mService.addWindow(
                    window,
                    seq, 
                    viewVisibility,
                    displayId,
                    outStableInsets, 
                    outOutsets,
                    outInputChannel
        );
 }

这里的 WindowSession 是一个 IBinder 对象,连接系统进程 WindowManagerService,前面说过,WindowManagerService 管理整个 Android 系统的 Window,因此只有在 WindowManagerService 这里“登记”的 Window,才算是真正添加完成。进入 WindowManagerService 的 addWindow 方法中,可以看到大量的添加 Window 最终步骤的代码细节,至此,整个添加 Window 的大致流水帐就过了一遍。

Window 的 remove 和 update

Window 的 remove 和 update 和 add 操作相似,具体的顺序均为:

romove

WindowManager - > WindowManagerImpl -> WindowManagerGlobal -> ViewRootImpl .die() -> ViewRootImpl.dispatchDetchedFromWindow() -> WindowSession.removeWindow()

update

WindowManager - > WindowManagerImpl -> WindowManagerGlobal -> ViewRootImpl .setLayoutParams() -> ViewRootImpl.pokeDrawLockIfNeeded() -> WindowSession.pokeDrawLock(Window)

总结起来,Window 通过 ViewRootImpl 管理整个 View 层次,又总是最终通过 ViewRootImpl 调用 IBinder 对象 WindowSession 的方法,将最终步骤交付到 WindowManagerService 处。