[译]理解 RenderThread

原文连接:Understanding the RenderThreadgit

RenderThread 是在 Android Lollipop 中新加入的组件。关于该组件的文档描述甚少,仅有一个模糊的定义:github

RenderThread 是一个由系统管理的线程。较之 UI 线程的延迟,RenderThread 的动画播放更为流畅。canvas

为了解 RenderThread 的工做机制,须要介绍一些基本的概念。安全

当启动硬件加速后,Android 使用 “display list” 组件进行绘制而非直接使用 CPU 绘制每一帧。Display List 是一系列绘制操做的记录,抽象为 RenderNode 类。bash

这样间接的进行绘制操做的优势颇多:异步

  1. Display List 能够按需屡次绘制而无须同业务逻辑交互。
  2. 特定的绘制操做(如 translation, scale 等)能够做用于整个 display list 而无须从新分发绘制操做。
  3. 当知晓了全部绘制操做后,能够针对其进行优化:例如,全部的文本能够一块儿进行绘制一次。
  4. 能够将对 display list 的处理转移至另外一个线程(非 UI 线程)。

最后一点刚好是 RenderThread 负责的:在 UI 线程外执行优化操做与将绘制操做分发给 GPU。ide

在 Lollipop 以前,执行“重”操做的同时对 View 属性执行动画(例如在 Activity 间执行 transition 动画)几乎不可能。而 Lollipop 及以上的 Android 版本,这类动画以及其它一些效果(例如 ripple )却能够流畅的执行。这样的黑科技便源自 RenderThread 的帮助。优化

渲染工做的真正执行者是 GPU,而 GPU 对于动画一无所知:执行动画的惟一途径即是将每一帧的不一样绘制操做分发给 GPU,但该逻辑自己不能在 GPU 上执行。而若是在 UI 线程执行该操做,任意的重操做都将阻塞新的绘制指令及时分发,因而动画便出现了延迟。动画

如前文所述,RenderThread 能够处理 display list 流程的某些部分,但要注意到 display list 的建立和修改仍须要在 UI 线程中执行。this

那么动画是怎样从不一样的线程中进行更新的呢?

开启硬件加速后,CanvasDisplayListCanvas 类实现,该类重载了部分绘制方法,方法参数类型使用 CanvasProperty 对象替换原有的基本类型(例如用 CanvasProperty<Float> 替换 float 类型),CanvasProperty 是原有类型的包装类。这样,dislplay list 及其对应的绘制操做即可以在 UI 线程中建立,而绘制方法的参数能够经过 CanvasProperty 的映射动态地、经过 RenderThread 异步地修改。

实现上述操做还需一步:CanvasProperty 的值须要经过 RenderNodeAnimator 执行动画操做,RenderNodeAnimator 中包含了动画的配置及初始值。

此类动画有一些有趣的特性:

  • 目标 DisplayListCanvas 须要手动设置且不可修改
  • 该动画发送后无须关注:动画开始执行后只能取消(没有暂停和恢复)且没法知晓此刻的属性值
  • 能够设置自定义插值器,插值器将在 RenderThread 执行
  • start delay 通常在 RenderThread 上等待

时至今日,能够经过 RenderThread 执行的动画以下:

View 属性 (经过 View 的 animate 方法执行)

  • Translation (X, Y, Z)
  • Scale (X, Y)
  • Rotation (X, Y)
  • Alpha
  • Circular reveal animation (经过 ViewAnimationUtilscreateCircularReveal 执行)

Canvas 方法与 Canvas 属性

  • drawCircle(centerX, centerY, radius, paint)
  • drawRoundRect(left, top, right, bottom, cornerRadiusX, cornerRadiusY, paint)

Paint 属性

  • Alpha
  • Stroke width

彷佛 Google 仅将 Material Design 动画中所需的绘制操做进行了封装。

在 Android N 及以上,RenderThread 的能力的得以拓展(例如,AnimatedVectorDrawable 将使用 RenderThread 执行动画),也许从此 RenderThread 会成为开放 API。

简而言之,我能够在 RenderThread 执行动画吗?

依据文档,不能够。 除非使用 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);
}
复制代码
相关文章
相关标签/搜索