上一篇文章https://blog.csdn.net/zxm528/article/details/123226033?spm=1001.2014.3001.5501,我们分析了Android中Activity,Window以及View三者之间的逻辑关系。最终分析到是我们设置了页面的布局是通过ViewRootImpl#setView()方法实现的,那么我们就会想知道我们设置的布局是怎么渲染的。
在上一篇文章中,我们了解到ViewRootImpl自身不是View控件,它是View视图树的顶层设计,起到承上启下的作用,将Window与View联系起来。一方面ViewRootImpl通过Binder通信机制,远程调用WindowSession将View添加到Window中。另一方面ViewRootImpl在添加View之前又调用了requestLayout方法,执行一次完整的View树渲染流程。
我们还是接着上一篇文章中ViewRootImpl#setView方法切入吧。
我们已经说过位置1处,requestLayout方法第一次被调用就是请求布局的绘制的操作,其中就包括View的测量,布局以及绘制等,我们看一下具体代码:
上图位置1处是用于检测当前线程是否是主线程,如果不是就会抛出“Only the original thread that created a view hierarchy can touch its views.”的异常。位置2处是成员变量的更改,该参数决定了后续是否需要执行measure和layout相关的操作。
最后我们看一下方法里最后执行的scheduleTraversals()方法。具体代码如下:
位置1处,是对应的消息队列发送了一条同步屏障(synchronization barrier)。该方法的源码我们在这里就不在解释了,这些又涉及到消息队列以及同步屏障的内容,不在本文章的讨论范围。大致解释一下就是该方法向消息队列中添加了一个没有target的Message。在MessageQueue#next方法中获取message时,如果该message没有target,那么在一定时间内会跳过同步消息优先执行异步消息。就是通过该方法,保证了UI的绘制优先执行。位置2处是Choreographer执行了postCallback方法,注意此处还有一个变量mTraversalRunnable,一会在说。看一下该方法的代码:
在postCallbackDelayedInternal()方法里,最终message被设置为异步类型,然后Handler将该消息发送到了消息队列中。
还记得上面说的变量mTraversalRunnable么,它是一个线程,在其线程中有一个方法doTravelsal,我们看一下其源码:
上图中的标签1处,是消息队列移除了同步屏障(synchronization barrier);标签2处,就是核心方法,该方法就是真正的开始了View的绘制流程;测量,布局,绘制;
下面我们看一下performTraversals()方法的源码,该方法代码比较多,这里就抽取一下核心代码,进行分析:
上一篇的文章中我们知道:该方法中mView就是DecorView.标签1处:是布局的测量逻辑;标签2处:是布局的逻辑;标签3处:是布局的绘制逻辑;标签4处:重新进行一遍上面的步骤。下面我们进行详细说明。
- 首先是ViewRootImpl#measureHierarchy()方法。从源码我们可以看到主要逻辑是以下关键代码:
这个方法中,通过getRootMeasureSpec方法获取了根View的MeasureSpec,实际上MeasureSpec中的宽高此处获取的是Window的宽高。下一步就是执行performMeasure()方法了。我们看一下源码:
上图标签1处,最终是View拿到了Window的尺寸信息,在measure方法里最终执行了onMeasure()方法,我们通过前面文章的分析,已经知道这里的mView就是DecorView,那么也就是执行的是DecorView的onMeasure方法。因为DecorView继承的是FrameLayout,那么也会执行FrameLayout的onMeasure()方法,并递归调用子控件的onMeasure()
- ViewRootImpl#performLayout方法。下面看一下该方法的源码:
标注位置处,layout方法最终会执行onLayout()方法。我们已经知道,host就是成员变量mView,也就是执行DecorView的onLayout方法,阅读下源码我们知道:它是先执行了FrameLayout的onLayout方法,然后执行的是每个child控件的onLayout方法,最后执行的是DecorView自己的逻辑。
- ViewRootImpl#performDraw方法。下面我们看一下该方法的源码:
我们看到核心方法是draw方法,我们分析一下draw()方法的核心代码。
我们先看一下标签1处,表示如果App开启了硬件加速功能,则会走标签1处的代码进行绘制。标签2处,表示使用软件绘制。下面我们看一下drawSoftware()方法的代码
标签1处,是调用了DecorView的draw()方法。我们看源码知道其实DecorView的draw()方法并没有多少逻辑,主要的还是调用了View的draw()方法。
标签2处,使用的是surface对象的方法进行处理,实际上是Canvas将内容提交给SurfaceFlinger进行合成处理。
默认的情况下软件绘制没有采用GPU渲染的方式,drawSoftware工作完全是由CPU来完成的。
总结一下:
到这里,View的渲染的大致逻辑就这些了。下面总结一下,ViewRootImpl主要负责UI的渲染,其核心方法是performTraversals方法,执行该方法会依次执行measure,draw,layout操作。