原文连接:Understanding the RenderThreadgit
RenderThread 是在 Android Lollipop 中新加入的组件。关于该组件的文档描述甚少,仅有一个模糊的定义:github
RenderThread 是一个由系统管理的线程。较之 UI 线程的延迟,RenderThread 的动画播放更为流畅。canvas
为了解 RenderThread 的工做机制,须要介绍一些基本的概念。安全
当启动硬件加速后,Android 使用 “display list” 组件进行绘制而非直接使用 CPU 绘制每一帧。Display List 是一系列绘制操做的记录,抽象为 RenderNode
类。bash
这样间接的进行绘制操做的优势颇多:异步
最后一点刚好是 RenderThread 负责的:在 UI 线程外执行优化操做与将绘制操做分发给 GPU。ide
在 Lollipop 以前,执行“重”操做的同时对 View 属性执行动画(例如在 Activity 间执行 transition 动画)几乎不可能。而 Lollipop 及以上的 Android 版本,这类动画以及其它一些效果(例如 ripple )却能够流畅的执行。这样的黑科技便源自 RenderThread 的帮助。优化
渲染工做的真正执行者是 GPU,而 GPU 对于动画一无所知:执行动画的惟一途径即是将每一帧的不一样绘制操做分发给 GPU,但该逻辑自己不能在 GPU 上执行。而若是在 UI 线程执行该操做,任意的重操做都将阻塞新的绘制指令及时分发,因而动画便出现了延迟。动画
如前文所述,RenderThread 能够处理 display list 流程的某些部分,但要注意到 display list 的建立和修改仍须要在 UI 线程中执行。this
那么动画是怎样从不一样的线程中进行更新的呢?
开启硬件加速后,Canvas
由 DisplayListCanvas
类实现,该类重载了部分绘制方法,方法参数类型使用 CanvasProperty
对象替换原有的基本类型(例如用 CanvasProperty<Float>
替换 float
类型),CanvasProperty
是原有类型的包装类。这样,dislplay list 及其对应的绘制操做即可以在 UI 线程中建立,而绘制方法的参数能够经过 CanvasProperty
的映射动态地、经过 RenderThread 异步地修改。
实现上述操做还需一步:CanvasProperty
的值须要经过 RenderNodeAnimator
执行动画操做,RenderNodeAnimator
中包含了动画的配置及初始值。
此类动画有一些有趣的特性:
DisplayListCanvas
须要手动设置且不可修改时至今日,能够经过 RenderThread 执行的动画以下:
animate
方法执行)ViewAnimationUtils
的 createCircularReveal
执行)彷佛 Google 仅将 Material Design 动画中所需的绘制操做进行了封装。
在 Android N 及以上,RenderThread 的能力的得以拓展(例如,AnimatedVectorDrawable
将使用 RenderThread 执行动画),也许从此 RenderThread 会成为开放 API。
依据文档,不能够。 除非使用 View.animate
或是 ViewAnimationUtils.createCircularReveal
。
经过 Hack ,能够实现。 对于未开放的组件,咱们均可以经过反射获取对相关类、方法的引用,经过包装类以保证类型安全,反射失败时提供反馈等等。
笔者实现了使用 RenderThread 执行动画的库。
使用该库十分简单,仅需 3 步:
CanvasProperty<Float> centerXProperty;
CanvasProperty<Float> centerYProperty;
CanvasProperty<Float> radiusProperty;
CanvasProperty<Paint> paintProperty;
Animator radiusAnimator;
Animator alphaAnimator;
@Override
protected void onDraw(Canvas canvas) {
if (!animationInitialised) {
// 1. create as many CanvasProperty as needed with the initial animation values
centerXProperty = RenderThread.createCanvasProperty(canvas, initialCenterX);
centerYProperty = RenderThread.createCanvasProperty(canvas, initialCenterY);
radiusProperty = RenderThread.createCanvasProperty(canvas, initialRadius);
paintProperty = RenderThread.createCanvasProperty(canvas, paint);
// 2. create one or more Animator with the properties you want to animate
radiusAnimator = RenderThread.createFloatAnimator(this, canvas, radiusProperty, targetRadius);
alphaAnimator = RenderThread.createPaintAlphaAnimator(this, canvas, paintProperty, targetAlpha);
radiusAnimator.start();
alphaAnimator.start();
}
// 3. draw to the Canvas
RenderThread.drawCircle(canvas, centerXProperty, centerYProperty, radiusProperty, paintProperty);
}
复制代码