读书笔记:理解 Android Window 的添加过程

来源:互联网 发布:网络诈骗安全用语 编辑:程序博客网 时间:2024/06/06 19:31

前言

我们知道,在 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 处。