View 的绘制
承接上文 View 的测量与布局,在整个 View 树完成测量和布局的流程之后,接着执行的是最后一步:绘制渲染 draw
过程。整体流程是:
measure
-> layout
-> draw
。
发起点同样是 ViewRootImpl,整体的时序图示意如下:
由于 View 的渲染绘制过程涉及到硬件层面,整体的流程更为之复杂。
硬件加速绘制和软件绘制
View 的绘制共分为两种:硬件加速绘制和软件绘制。两种绘制方式在 Android 系统中有以下的差别:
- 硬件加速绘制是借助能高效处理图形计算的 GPU 来完成,而软件绘制则是仅依赖 CPU 来完成。
- 硬件加速绘制在绘制区域的构建中、渲染过程中做了大量的优化
从 Android 4.0 开始,系统默认开启硬件加速。显然是因为一般情况下,View 通过硬件加速绘制能有更好的图形性能和体验。可通过以下代码判断 View 是否已开启硬件加速:
View.isHardwareAccelerated()
两种绘制的分歧点
承接上文,View 绘制的发起点是在 ViewRootImpl 的 performDraw
中,其后会判断
View 的硬件加速是否已启用。若启用,则使用硬件加速绘制,否则则使用软件绘制,整体时序图如下:
frameworks/base/core/java/android/view
- ViewRootImpl.java
- View.java
- ThreadedRenderer.java
细看下 ViewRootImpl 中 draw
方法中硬件加速绘制与软件绘制的分歧点实现:
private boolean draw(boolean fullRedrawNeeded) {
...
// 判断是否开启了硬件加速
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
...
// 使用硬件加速绘制
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
...
// 否则通过软件绘制
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
}
}
接下来会着重分析硬件加速绘制
的一系列过程。
硬件加速绘制
Android 系统对于硬件加速绘制流程,有一点很重要的优化是,引入了显示列表(DisplayList
)的概念。
显示列表,指每个
承接上文,View 的硬件加速绘制是进一步通过 ThreadedRenderer
来完成。其会逐步调用到 View 的
View 树的 DrawOp 集的构建
RenderNode用于构建硬件加速的渲染层次结构。 每个RenderNode都包含一个显示列表以及一组影响显示列表呈现的属性。 默认情况下,RenderNodes在内部用于所有View,通常不直接使用。 RenderNode用于将复杂场景的渲染内容分成较小的片段,然后可以更便宜地对其进行单独更新。 更新场景的一部分仅需要更新少量RenderNode的显示列表或属性,而无需从头开始重新绘制所有内容。 仅在更改其内容时,RenderNode仅需要重新记录其显示列表。 无需通过转换属性重新记录显示列表,也可以转换RenderNodes
RenderThread 渲染 UI 到 Graphic Buffer
Title: I’m title ChildView->ViewGroup: invalidate() ViewGroup->ViewGroup: invalidateChild(dirty) ViewGroup->…: invalidateChildInParent(dirty) …->…: … …->ViewRootImpl: invalidateChildInParent(dirty) ViewRootImpl->ViewRootImpl: invalidateChildInParent(dirty) ViewRootImpl->ViewRootImpl: invalidateRectOnScreen(dirty)\nmDirty.union(dirty) ViewRootImpl->ViewRootImpl: scheduleTraversals() ViewRootImpl->ViewRootImpl: performTraversals() ViewRootImpl->ViewRootImpl: perfromDraw() ViewRootImpl->ViewRootImpl: draw() ViewRootImpl->ViewRootImpl: drawSoftware(surface, dirty)
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
// Draw with software renderer.
final Canvas canvas;
...
canvas = mSurface.lockCanvas(dirty);
...
try {
...
mView.draw(canvas);
} finally {
...
surface.unlockCanvasAndPost(canvas);
}
}
Title: Software canvas
CompatibleCanvas->CompatibleCanvas:
ViewRootImpl->Surface: drawSoftware() Surface->Surface: lockCanvas() Surface->Surface\n(native): nativeLockCanvas() Surface\n(native)->Surface\n(native): lock(&buffer)