android源码宇宙-ViewRootImpl

270 阅读3分钟

ViewRootImpl分析

ViewRootImpl是用来指导我们view体系测量、布局、绘制的一个类,当需要重新刷新的时候,ViewRootImpl会负责去申请vsync信号,然后在收到vsync信号后结合wms完成view的测量、布局、绘制。

源码

主流程代码

通过以前wms的学习我们知道ViewRootImpl的创建是发生在WindowManagerGlobal.addView方法中的,那么我们就从WindowManagerGlobal.addView方法开始我们的分析。

  1. ViewRootImpl的创建过程

image.png

  1. ViewRootImpl.setView方法

setView方法中的requestLayout会去请求重新绘制屏幕内容,当我们主动调用requestLayout、invalidate、启动动画的时候都会走重新绘制屏幕
image.png

  1. 调用了requestLayout后就会走我们熟悉的Choreographer请求vsync信号的过程,当vsync信号拿到后就会回调TraversalRunnable
  2. 查看TraversalRunnable

image.png

  1. doTraversal

image.png

  1. performTraversals

在performTraversals方法中会依次调用measureHierarchy、performLayout、performDraw方法完成view的测量、布局绘制

performTraversals方法是如何工作的

image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
此处省略一大段SurfaceView的处理逻辑,将来有必要再说

总的来说,在ViewRootImpl来说,SurfaceView的绘制逻辑优先级是高于我们的view体系的,它会在我们的view绘制之前先执行,并且拥有自己的绘制线程。

image.png
image.png
image.png
image.png
image.png

performMeasure

  1. ViewRootImpl.performMeasure

image.png

  1. View.measure

image.png
image.png

  1. onMeasure

onMeasure中会设置默认的尺寸
image.png

performLayout

image.png
image.png
image.png

小结:ViewRootImpl.performLayout方法会自顶向下进行布局,performLayout方法会进行两次布局,第一次布局会调用DecorView.layout方法。当第一次方法完成后会去布局的请求池去拿最新的请求,如果在布局的过程中有新的布局请求,则我们需要进行重新测量和布局。 当第二次测量和布局完成后,会再次去查看布局请求池中的请求,此时如果又发现了新的请求,则新的请求会等待下一帧到来的时候再去参与到下一帧的绘制中。

performDraw

  1. ViewRootImpl.performDraw

image.png
image.png
流程太多细节代码暂时没看那么透,下一步主要的代码在ThreadedRenderer.draw
image.png

  1. ThreadedRenderer.draw

image.png

  1. ThreadedRenderer.updateRootDisplayList

image.png

  1. ThreadedRenderer.updateViewTreeDisplayList

image.png

  1. View.updateDisplayListIfDirty

image.png
image.png

  1. View.draw

image.png

下图是绘制边缘内容,流程和调用方法与view本身绘制相同,所以后面的代码不贴了

image.png

View.requestLayout

image.png

其它

某个View.requestLayout快速多次调用,是否会触发多次布局

答:不会
image.png

小结:单个View调用requestLayout后,在View.layout方法执行前,无法再次向父类传递mParent.requestLayout请求。吐槽:源码中逻辑运算符操作真难读

是否每次调用View.requestLayout都会 重新测量所有View的尺寸,即:调用所有View.onMeasure方法

答:不会

  1. View.requestLayout中设置强制布局的标志位

这个标志位只会对当前View有效
image.png

  1. View.measure根据PFLAG_FORCE_LAYOUT标志位判断进行测量

image.png

总结:当我们调用某个View.requestLayout的时候会设置一个标志位,当ViewRootImpl收到我们的布局请求后,向下遍历view的时候会走所有子View的measure来测量,这些子View如果没有设置过该标志位,则不会走测量宽度的逻辑。