Android 绘制流程
2021-02-03 本文已影响0人
科技猿人
Read The Fucking Source Code
引言
Android的绘制流程是什么?
onMeasure,onLayout,onDraw???这已经是绘制分发流程了。
现在一起探索绘制流程起源
源码版本(Android Q — API 29)
本文涉及应用启动流程
1. 绘制流程起源概览
- 大家都知道绘制是从ViewRootImpl进行分发的,由DecorView来进行绘制分发。
- 那么DecorView是哪儿来的?
- ViewRootImpl哪儿来的?
- 应用启动后,在什么时刻进行绘制?
- 带着这些问题,一起来探索 绘制流程起源
2. 绘制流程起源刨析
2.1 创建PhoneWindow
2.2 创建DecorView
2.3 WindowManager添加DecorView
2.4 创建ViewRootImpl
2.5 添加视图到WMS
2.6 Vsync时钟触发绘制流程
3. Android绘制流程起源汇总
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方法。所以可以监控。