原文連接: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);
}
複製代碼