Android 绘制流程

Read The Fucking Source Code

引言

Android的绘制流程是什么?
onMeasure,onLayout,onDraw???这已经是绘制分发流程了。
现在一起探索 绘制流程起源

源码版本(Android Q — API 29)

前置依赖 【Android App启动流程】

1. 概览

  • 大家都知道绘制是从ViewRootImpl进行分发的,由DecorView来进行绘制分发。
  • 那么DecorView是哪儿来的?
  • ViewRootImpl哪儿来的?
  • 应用启动后,在什么时刻进行绘制?
  • 带着这些问题,一起来探索 绘制流程起源

2. 刨析

2.1 创建 PhoneWindow

创建PhoneWindow

2.2 创建 DecorView

创建DecorView

2.3 添加 DecorView

WindowManager添加DecorView

2.4 创建 ViewRootImpl

创建ViewRootImpl

2.5 添加视图到 WMS

添加视图到WMS

2.6 Vsync 时钟触发绘制流程

Vsync时钟触发绘制流程

3. 汇总

Activity绘制流程

4. 思考

ViewRootImpl和DecorView的到底是怎样的关系?

  • Android中Window是View的载体,每个Window下都挂着一棵View树,每个Window的最顶层View是DecorView。
  • ViewParent指明了父View要实现的职责,ViewGroup就实现了这个接口,ViewGroup是包裹View的对象。
  • ViewRootImpl实现了ViewParent,接管了DecorView的ViewParent职责,关于View的对外工作(比如刷新时钟/input输入)都需要ViewRootImpl来处理。
  • ViewRootImpl可以理解为View的外交大臣。因为ViewRootImpl并不是一个真正的View,它只是接管了DecorView的ViewParent职责,把ViewRootImpl说成是DecorView的父亲并不准确。应该是DecorView的干爹(果然还是干爹香啊)。

View的绘制流程主要是指measure、layout、draw。简述一下各自职责。

  • measure确定View的测量宽/高
  • layout确定View的最终宽/高和四个顶点的位置
  • draw则将View绘制到屏幕上

首次 View 的绘制流程是在什么时候触发的?

  • ActivityThread.handleResumeActivity 里触发的。
  • 最终通过 WindowManagerImpl.addView -> WindowManagerGlobal.addView -> ViewRootImpl.setView -> ViewRootImpl.requestLayout 就触发了第一次 View 的绘制。

DecorView 的布局是什么样的?

  • 对于 Activity 的层级,Activity -> PhoneWindow -> DecorView -> [title_bar, content],其中 DecorView里包括了 title_bar 和 content 两个 View,不过这个是默认的布局,实际上根据不同的主题样式,DecorView 对应有不同的布局。
  • DecorView默认布局是:R.layout.screen_simple 。那DecorView的布局是根据什么来设置的?theme…

Activity、PhoneWindow、DecorView、ViewRootImpl 的关系?

  • PhoneWindow 其实是 Window 的唯一子类,是 Activity 和 View 交互系统的中间层,而 DecorView 是整个 View 层级的最顶层,ViewRootImpl 是 DecorView 的 parent,但是他并不是一个真正的 View,只是继承了 ViewParent 接口,用来掌管 View 的各种事件,包括 requestLayout、invalidate、dispatchInputEvent 等等。

为什么在 Activity 的生命周期里无法获得测量宽高?有什么方法可以解决这个问题吗?

  • 因为 View 的测量过程和 Activity 的生命周期没有任何关系。因为它在生命周期结束之后才开始。
  • 解决方案 1:View还没开始分发测量绘制时,就会创建一个长度为4的HandlerAction(包含Runnable)数组。ViewRootImpl在调用AttchInfo对象的dispatchAttachedToWindow()方法时,将HandlerActionQueue中的缓存Runnable取出加入到ViewRootImpl的Handler中,因为绘制刷新的事件使用了Handler的同步屏障(也就是高优先级的消息),所以只能等待测量绘制结束后,才会去处理之前添加的缓存Runnable,所以在Runnable的处理中,再去拿测量宽高就是正常的。
  • 方案 2:通过设置ViewTreeObserver的OnDrawListener来进行获取(举例:mRootView.getViewTreeObserver().addOnDrawListener(省略))。因为ViewRootImpl在performMeasure、performLayout等方法后会调用mAttchInfo.mTreeObserver.dispatchOnPreDraw方法。所以可以监控。

小编的扩展链接

《Android 视图模块 全家桶》

猜你喜欢

转载自juejin.im/post/6979873581698121736