Android中的View是如何渲染的?

上一篇文章icon-default.png?t=M276https://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方法切入吧。

Android中的View是如何渲染的?_第1张图片

我们已经说过位置1处,requestLayout方法第一次被调用就是请求布局的绘制的操作,其中就包括View的测量,布局以及绘制等,我们看一下具体代码:

Android中的View是如何渲染的?_第2张图片

上图位置1处是用于检测当前线程是否是主线程,如果不是就会抛出“Only the original thread that created a view hierarchy can touch its views.”的异常。位置2处是成员变量的更改,该参数决定了后续是否需要执行measure和layout相关的操作。

最后我们看一下方法里最后执行的scheduleTraversals()方法。具体代码如下:

Android中的View是如何渲染的?_第3张图片

位置1处,是对应的消息队列发送了一条同步屏障synchronization  barrier。该方法的源码我们在这里就不在解释了,这些又涉及到消息队列以及同步屏障的内容,不在本文章的讨论范围。大致解释一下就是该方法向消息队列中添加了一个没有target的Message。在MessageQueue#next方法中获取message时,如果该message没有target,那么在一定时间内会跳过同步消息优先执行异步消息。就是通过该方法,保证了UI的绘制优先执行位置2处是Choreographer执行了postCallback方法,注意此处还有一个变量mTraversalRunnable,一会在说。看一下该方法的代码:

Android中的View是如何渲染的?_第4张图片

postCallbackDelayedInternal()方法里,最终message被设置为异步类型,然后Handler将该消息发送到了消息队列中。

还记得上面说的变量mTraversalRunnable么,它是一个线程,在其线程中有一个方法doTravelsal,我们看一下其源码:

Android中的View是如何渲染的?_第5张图片

上图中的标签1处,是消息队列移除了同步屏障synchronization  barrier);标签2,就是核心方法,该方法就是真正的开始了View的绘制流程;测量,布局,绘制;

下面我们看一下performTraversals()方法的源码,该方法代码比较多,这里就抽取一下核心代码,进行分析:

Android中的View是如何渲染的?_第6张图片

上一篇的文章中我们知道:该方法中mView就是DecorView.标签1处:是布局的测量逻辑;标签2处:是布局的逻辑;标签3处:是布局的绘制逻辑;标签4处:重新进行一遍上面的步骤。下面我们进行详细说明。

  • 首先是ViewRootImpl#measureHierarchy()方法。从源码我们可以看到主要逻辑是以下关键代码:

Android中的View是如何渲染的?_第7张图片

这个方法中,通过getRootMeasureSpec方法获取了根View的MeasureSpec,实际上MeasureSpec中的宽高此处获取的是Window的宽高。下一步就是执行performMeasure()方法了。我们看一下源码:

Android中的View是如何渲染的?_第8张图片

上图标签1,最终是View拿到了Window的尺寸信息,在measure方法里最终执行了onMeasure()方法,我们通过前面文章的分析,已经知道这里的mView就是DecorView,那么也就是执行的是DecorView的onMeasure方法。因为DecorView继承的是FrameLayout,那么也会执行FrameLayout的onMeasure()方法,并递归调用子控件的onMeasure()

  • ViewRootImpl#performLayout方法下面看一下该方法的源码

Android中的View是如何渲染的?_第9张图片

标注位置处,layout方法最终会执行onLayout()方法。我们已经知道,host就是成员变量mView,也就是执行DecorView的onLayout方法,阅读下源码我们知道:它是先执行了FrameLayoutonLayout方法,然后执行的是每个child控件的onLayout方法,最后执行的是DecorView自己的逻辑。

  • ViewRootImpl#performDraw方法。下面我们看一下该方法的源码:

Android中的View是如何渲染的?_第10张图片

我们看到核心方法是draw方法,我们分析一下draw()方法的核心代码

Android中的View是如何渲染的?_第11张图片

我们先看一下标签1,表示如果App开启了硬件加速功能,则会走标签1处的代码进行绘制标签2,表示使用软件绘制。下面我们看一下drawSoftware()方法的代码

Android中的View是如何渲染的?_第12张图片

标签1,是调用了DecorViewdraw()方法。我们看源码知道其实DecorView的draw()方法并没有多少逻辑,主要的还是调用了Viewdraw()方法

标签2处,使用的是surface对象的方法进行处理,实际上是Canvas将内容提交给SurfaceFlinger进行合成处理

默认的情况下软件绘制没有采用GPU渲染的方式,drawSoftware工作完全是由CPU来完成的。

总结一下:

到这里,View的渲染的大致逻辑就这些了。下面总结一下,ViewRootImpl主要负责UI的渲染,其核心方法是performTraversals方法,执行该方法会依次执行measure,draw,layout操作。

你可能感兴趣的:(Android,源码分析,Android,自定义View,android,ViewRootImpl,源码,View渲染)