Android View 绘制流程

  • 1. 讲一讲 View 的绘制流程
  • 2. Android 中 xml 布局怎么显示到屏幕上的?
    • 几个关键点:
    • 2.1 什么是 ViewRootImpl
    • 2.2 什么是 Activity?从视图角度分析
    • 2.3 什么是Window?
    • 2.4 什么是DecorView?
    • 2.5 Activity,window,View 三者之间的关系是什么?
    • 2.6 Activity、PhoneWindow、DecorView 关系图
  • 3. 再来看看 普通的View 是怎么绘制的
    • 3.1 ViewRootImpl 是如何绘制 View 的
  • 4. 延申
    • 4.1 为什么我在 onCreate 中调用 View.post 方法可以得到 View 的宽高呢
    • 4.2 invaliate 和 requestlayout 方法的区别
  • 参考链接

Android View 绘制流程_第1张图片

1. 讲一讲 View 的绘制流程

  • 简单的可以说,如 measure,layout,draw 分别对应测量,布局,绘制三个过程
  • 深一些可以引申出 Handler,同步屏障,View 的事件传递,甚至 activity 的启动过程

总体而言,Android 的绘制基本可以分为

  • 顶级 View 的绘制 --> 也就是 ViewrootImpl,在这姑且先将它理解为 Activity
  • Viewgroup 的绘制
  • View 的绘制

按顺序讲的话就得先讲 View 是怎么绘制到屏幕上的,或者 Activity 怎么加载布局的

2. Android 中 xml 布局怎么显示到屏幕上的?

在整个 Activity 的生命周期中,setContentView 是在 onCreate 中调用的,它实现了对资源文件的解析,完成了 xml 文件到 View 的转化。

那么 View 真正开始绘制是在哪个生命周期呢?

答案是 onResume 结束后

Android View 绘制流程_第2张图片

几个关键点:

  • onResume 之后,从 Activity 中的 Window 实例中获取 Decorview
  • 调用 activitywindowmanageraddView 方法,将 decorView 传入到 ViewRootImplsetView 方法中
  • 通过 ViewRootImpl.setView() 来完成 View 的绘制

问题又来了,什么是 ViewRootImpldecorView 呢?setView 到底有什么魔法,为什么他就能完成 View 的绘制工作呢?

  • Activity、Window、View 之间的关系

2.1 什么是 ViewRootImpl

从结构上来看,ViewRootImplViewGroup 其实是一种东西

/**
 * The top of a view hierarchy, implementing the needed protocol between View and the WindowManager.  

 * This is for the most part an internal implementation detail of {@link WindowManagerImpl}.
 * {@hide}
 */
public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
	...
}
//A  ViewGroup is a special view that can contain other views (called children.) 
//The view group is the base class for layouts and views containers. 
//This class also defines the LayoutParams class which serves as the base class for layouts parameters.
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
	...
}
  • 相同点

    • 它们都继承了 ViewParent
    • ViewParent 是一个接口,定义了一些父 View 的基本行为,比如 requestlayoutgetparent 等。
  • 不同点

    • ViewRootImpl 并不会像 ViewGroup 一样被真正绘制在屏幕上
    • Activity 中,它是专门用来绘制 DecorView 的,核心方法是 setView

2.2 什么是 Activity?从视图角度分析

  • Activity 并不负责视图控制,它只是控制生命周期和处理事件。真正控制视图的是 Window。一个 Activity 包含了一个 WindowWindow才是真正代表一个窗口。
  • Activity就像一个控制器,统筹视图的添加与显示,以及通过其他回调方法,来与 Window、以及 View 进行交互。

2.3 什么是Window?

  • Window 是一个抽象类,实际在 Activity 中持有的是其子类 PhoneWindowPhoneWindow 中有个内部类DecorView,通过创建 DecorView 来加载 Activity 中设置的布局 R.layout.activity_main
  • Window 是视图的承载器,内部持有一个 DecorView,而这个 DecorView才是 view 的根布局。
  • Window 通过 WindowManagerDecorView 加载其中,并将 DecorView 交给 ViewRootimpl,进行视图绘制以及其他交互
/**
 * 顶级窗口外观和行为策略的抽象基类。An instance of this class 应用作 top-level view 添加到窗口管理器。
 * 它提供了标准的 UI 策略,例如背景、标题区域、默认键处理等。
 *
 * 

这个抽象类的唯一现有实现是 PhoneWindow, which you should instantiate when needing a * Window. */ public abstract class Window { ... /** * Retrieve the top-level window decor view (containing the standard * window frame/decorations and the client's content inside of that), which * can be added as a window to the window manager. * *

Note that calling this function for the first time "locks in" * various window characteristics as described in * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}.

* * @return Returns the top-level window decor view. */
public abstract @NonNull View getDecorView(); }

Android View 绘制流程_第3张图片

2.4 什么是DecorView?

DecorViewFrameLayout 的子类,它可以被认为是 Android 视图树的根节点视图

  • DecorView 作为顶级 View,一般情况下它内部包含一个竖直方向的 LinearLayout,在这个 LinearLayout 里面有上下三个部分,上面是个 ViewStub,延迟加载的视图(应该是设置ActionBar,根据 Theme 设置),中间的是标题栏(根据Theme设置,有的布局没有),下面的是内容栏。

  • 具体情况和Android版本及主体有关,以其中一个布局为例,如下所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    
    <ViewStub
        android:id="@+id/action_mode_bar_stub"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inflatedId="@+id/action_mode_bar"
        android:layout="@layout/action_mode_bar"
        android:theme="?attr/actionBarTheme" />

    <FrameLayout
        style="?android:attr/windowTitleBackgroundStyle"
        android:layout_width="match_parent"
        android:layout_height="?android:attr/windowTitleSize">

        <TextView
            android:id="@android:id/title"
            style="?android:attr/windowTitleStyle"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@null"
            android:fadingEdge="horizontal"
            android:gravity="center_vertical" />
    FrameLayout>

    <FrameLayout
        android:id="@android:id/content"
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="1"
        android:foreground="?android:attr/windowContentOverlay"
        android:foregroundGravity="fill_horizontal|top" />
LinearLayout>

Activity 中通过 setContentView 所设置的布局文件其实就是被加到内容栏之中的,成为其唯一子 View,就是上面的 idcontentFrameLayout 中,在代码中可以通过 content 来得到对应加载的布局。

ViewGroup content = (ViewGroup)findViewById(android.R.id.content);
ViewGroup rootView = (ViewGroup) content.getChildAt(0);

2.5 Activity,window,View 三者之间的关系是什么?

windowactivity 的一个成员变量,windowView 是“显示器”和“显示内容”的关系

  • Window 抽象类,PhoneWindow 唯一实现类,用于加载 Activity 的顶级 ViewDecorView
  • Window --> 顶级窗口外观和行为策略的抽象基类。
    • An instance of this class 应用作 top-level view 添加到窗口管理器。
    • 它提供了标准的 UI 策略,例如背景、标题区域、默认键处理等。
    • 相对 view 来说就是 显示器与现实内容 的区别
  • 一个 Activity 对应一个 Window 也就是 PhoneWindow,一个 PhoneWindow 持有一个 DecorView 的实例,DecorView 本身是一个 FrameLayout

2.6 Activity、PhoneWindow、DecorView 关系图

类似的问题,其实问的基本是一个东西。

activitysetContentView 方法实际上是就是交给 phonewindow 去做的。windowView 的关系可以类比为显示器和显示的内容。

Android View 绘制流程_第4张图片

3. 再来看看 普通的View 是怎么绘制的

Android View 绘制流程_第5张图片

上面分析到这:

  • onResume 之后,从 Activity 中的 Window 实例中获取 Decorview
  • 调用 activitywindowmanageraddView 方法,将 decorView 传入到 ViewRootImplsetView 方法中
  • 通过 ViewRootImpl.setView() 来完成 View 的绘制

问题又来了,setView 到底有什么魔法,为什么他就能完成 View 的绘制工作呢?

3.1 ViewRootImpl 是如何绘制 View 的

Android View 绘制流程_第6张图片

简单来说 setView 做了三件事

  • ① 检查绘制的线程是不是创建 View 的线程。这里可以引申出一个问题,View 的绘制必须在主线程吗?
  • ② 通过内存屏障保证绘制 View 的任务是最优先的
  • ③ 调用 performTraversals 完成 measure,layout,draw 的绘制

看到这里,ViewRootImpl 的绘制基本就完成了。其实这也是面试官希望听到的内容。考察的是面试者对 View 绘制体系的理解。

后续 ViewGroupView 的绘制其实是 performTraversals 对整个 ViewTree 的绘制。他们的关系可以用下面这张图表示

Android View 绘制流程_第7张图片

4. 延申

4.1 为什么我在 onCreate 中调用 View.post 方法可以得到 View 的宽高呢

Android View 绘制流程_第8张图片
View.post 会判断当前 View 是否已经被添加到 window 上。如果添加了则立即执行 runnable,如果没有被添加则先放到一个队列中存储起来,等添加到 window 上时再执行。

而 View 被测量完成后才会 attachToWindow。所以当 post 的 runnable 执行时,View 已经绘制完成了。

4.2 invaliate 和 requestlayout 方法的区别

前面我们说到,ViewRootImpl 作为顶级 View 负责 View 的绘制。所以简单来说,requestlayout 和 invaliate 最终都会向上回溯调用到 ViewRootImpl 的 postTranversals 方法来绘制 View。

不同的是 requestlayout 会绘制 View 的 measure,layout 和 draw 过程。invaliate 因为只添加了绘制 draw 的标志位,只会绘制 draw 过程。

参考链接

  • Android App启动流程
  • Android 绘制流程

你可能感兴趣的:(android)