window的创建过程

164 阅读4分钟

「这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战

前言:

window是一个抽象类,它的具体实现是PhoneWindow。创建一个window是非常简单的。我们只需要通过WindowManager即可完成。WindowMangaer是外界访问Window的入口。Window的具体实现位于WindowManagerService中。WIndowManager和WindowService的交互是一个IPC的过程。android中所有的视图都是通过Window来呈现的,不管是Activity,toast,Dialog,我们常见的setContentView在底层也是通过Window来完成的。

Window的内部机制

每一个Window都对应着一个View和ViewRootImpl.Window和view是通过ViewRootImpl来建立联系,因此Window并不是实际存在的,它是以View的形式存在,这点从WindowManager的定义也可以看出它继承ViewManager,ViewManager提供三个接口方法 addView,updateViewLayout,removeView都是针对View的,这说明View才是Window存在的实体。在实际使用中无法直接访问Window,对Window的访问必须通过WindowManager。WindowManage人具体实现是WindowManagerImpl在WindowManagerImpl中的操作如下:

private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

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

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

可以发现WindowMnagerImpl并没有真正的实现Window的三大操作,而是交给了WindowMnagerGlobal。WindwManagerGlobal的实现如下;


public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow, int userId) {
        //检查参数是否合法
    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");
    }

   //.......省略一些代码
    ViewRootImpl root;
    View panelParentView = null;
   //.......省略中间代码
        root = new ViewRootImpl(view.getContext(), display);

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

最终调用root.setView执行绘制操作

/**
 * We have one child
 */
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
        int userId) {
//....省略一些代码
// 最终调用了我们熟悉的requestLayout进行异步刷新请求
requestLayout();
//....省略一些代码
}
//requestLayout又交给scheduleTraversals
@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}

可以发现scheduleTraversals才是View真正的入口. 接着会通过WindowSession来完成Window的添加过程。windowSession的类型是IwindowSession,是一个binder对象。真正的实现类是Session。也就是Window的添加过程是一次IPC调用,在session内部会通过WindowManagerService来实现Window的添加。这样一来,Window的添加请求就交给了WindowManagerService去处理了。。。。其他删除,更新操作都差不多,可以自行查看。

Window创建

从上面可以学习到View依附于window上。Window的实现类是PhoneWindow.

1.window的创建是什么时候触发的? 要明白这个问题,我们就必须了解activity的启动过程。戳一戳前一篇Activity是如何启动的

从上篇文章我们会发现Activity启动最终会调用ActivityThread的performLaunchActivity方法,在performLaunchActivity中会通过类加载器从Instrumentation中获取Activity的信息来进行创建Activity的对象,度调用Activity中的attach方法关联运行过程中所依赖的一系列上下文环境变量。

在Activity的attach方法里系统会创建Activity所属的Window(phoneWindow)对象mWindow = new PhoneWindow(this, window, activityConfigCallback);

2. setConteView之后发生了什么

我们可以先看一个Activity的setContentView

public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

发现使用Activity将具体实现交给了Window处理。而Window的具体实现是phoneWindow,所以我们只需要看phoneWindow的相关逻辑即可。 PhoneWindow的setConteView方法大致分为以下几个步骤:

  1. 判断是否存在DecorView没有就调用generateDecor()进行创建DecorView,DecorView实现于Framelayout。 public class DecorView extends FrameLayout

  2. 调用generateLayout创建内容区域的布局参数

  3. 使用LayoutInflate解析布局文件并添加中decorView中

  4. 调用Window.CallBack中的onContentChanged通知Activity内容已经发生改变。(因为Activity实现了Window.CallBack)

经过上面的步骤,到这里为止DecorView已经创建完毕并且已经把我们创建的ContentView添加到DecorView中。但是这个时候DecorView还没有被WindowManager添加到Window中。最终会调用ActivityThread中的handlerResumeActivity方法。然后在handlerResumeActivity方法中再调用Activity的onResume和makeVisible使得Window显示出来。我们就可以操作UI了。