美文网首页Android进阶
【Android】Window/DecorView/ViewRo

【Android】Window/DecorView/ViewRo

作者: littlefogcat | 来源:发表于2021-05-23 06:54 被阅读0次

一、基本信息

Window

  • Window是一个抽象类,具体实现只有一个PhoneWindow类。
  • Window是一个抽象的概念,表示了一个窗口,是View Hierarchy(视图树)的容器;
  • 对Window的操作只能通过WindowManager,最终经IPC由WindowManagerService最终实现;
  • Window不直接管理View Hierarchy,而是通过ViewRootImpl;
  • 事件分发由Window直接下发给DecorView,而不通过ViewRootImpl;

ViewRootImpl

  • ViewRootImpl是View Hierarchy的根节点,是DecorView的parent,也是View Hierarchy的实际管理者;
  • ViewRootImpl管理整棵View树的相关事件,包括但不仅限于View的测量、布局、绘制等;
  • ViewRootImpl实现了View和WindowManager之间所需要的协议;
  • 实际上,View树的

DecorView

  • DecorView是最顶层的View,是所有View的祖先节点;
  • DecorView是一个FrameLayout;
层级关系

二、创建时机

Window

根据Activity启动流程,当流程进行到ActivityThread.performLaunchActivity的时候,会创建Activity实例,并调用其attach方法。在attach方法中,会创建Window的实例。

    final void performLaunchActivity(...args) {
        activity = mInstrumentation.newActivity(...);
        activity.attach(...);
        ...
    }
    final void attach(...args) {
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        ...
    }

也就是说,在Activity实例创建之初,Window就已经创建好了。

DecorView

DecorView在第一次调用Window.getDecorView的时候被创建。PhoneWindow类的getDecorView方法实现如下:

    public final @NonNull View getDecorView() {
        if (mDecor == null || mForceDecorInstall) {
            installDecor();
        }
        return mDecor;
    }

一般情况下,在onCreate回调中调用了setContentView方法,DecorView就被初始化了。
调用链为:
AppcompatActivity.setContentView ->
AppCompatDelegateImpl.setContentView ->
AppCompatDelegateImpl.ensureSubDecor ->
AppCompatDelegateImpl.createSubDecor ->
PhoneWindow.getDecorView ->
PhoneWindow.installDecor

最终,在PhoneWindow.installDecor方法中,DecorView被初始化:

    private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor(-1);
            ...
    }

    protected DecorView generateDecor(int featureId) {    
        ...
        return new DecorView(context, featureId, this, getAttributes());
    }

另外,如果不调用setContentView,DecorView同样也会在Activity.performCreate方法中较后的地方创建。

ViewRootImpl

ViewRootImpl是在WindowManagerGlobal的addView方法中被初始化的,并且也是在这里与DecorView进行绑定,成为DecorView的parent。调用链可以追溯到ActivityThread.handleResumeActivity中,在performResumeActivity调用之后,ViewRootImpl被创建。

    public void handleResumeActivity(...args) {
        ...
        final ActivityClientRecord r = performResumeActivity(...); // onResume
        ViewManager wm = a.getWindowManager();
        wm.addView(decor, l); // ViewRootImpl实例创建
    }

也就是说,ViewRootImpl是在onResume回调之后才进行初始化的。这可以在onResume中打印getWindow().getDecorView().getParent()证实。

小结

  • Window创建于ActivityThread.performLaunchActivity->Activity.attach,在onCreate之前;
  • DecorView一般创建于调用setContentView之后;如果不在onCreate中调用setContentView,DecorView会在onCreate之后(performCreate方法中)创建;
  • ViewRootImpl创建于ActivityThread.handleResumeActivity,在onResume之后;

三、其他

1. 第一次测绘流程何时执行?

中知道了,handleResumeActivity方法最终会调用WindowManagerGlobal.addView方法,在这里ViewRootImpl被创建,并且通过setView方法绑定DecorView,这些都发生在onResume之后。

在ViewRootImpl的setView方法中,又会调用requestLayout,在这里就会进行这个View树的第一次测绘。具体的方式是通过scheduleTraversals方法向Choreographer发送一个预定的消息,并在下一次屏幕刷新的时候调用doTraversal → performTraversals方法进行ViewTree的测量、布局和绘制。

从这一点我们可以知道,在Activity的onResume或之前的生命流程中调用View的getMesuredWidth或者getWidth都会返回0,因为这个时候还没有开始测量。

2. 为什么在onCreate中通过View.post可以获取View的宽高?View.post的流程是什么?

当View树还没有被测绘的时候,View.post会将这个Runnable发送给内部的一个消息队列(不是系统的消息队列)。这个消息队列中保存的Runnable会在下一次performTraversals的时候被执行;调用链为:
ViewRootImpl.performTraversals
DecorView.dispatchAttachedToWindow
... →
View.dispatchAttachedToWindow
executeActions
最终在executeActions方法中,Runnable对象被发送给ViewRootImpl的内部Handler执行。也就是说,当这个Runnable被执行的时候,已经至少经过一次测绘了,所以可以正确的获取到View的宽高;也说明了为什么通过View.post发送的Runnable会在主线中执行。

当View树已经被测绘过了View.post就会直接通过内部的mHandler发送消息,这个Handler是在attach的过程中跟着AttachInfo一起传递给View的,实质上就是ViewRootImpl的内部Handler。

相关文章

网友评论

    本文标题:【Android】Window/DecorView/ViewRo

    本文链接:https://www.haomeiwen.com/subject/vyjajltx.html