[译]通过 Hardware Layer 提升 Android 动画性能

heavydog 发布于8天前 阅读455次
0 条评论

欢迎转载,但请务必在明确位置注明文章出处! http://johnnyshieh.github.io/android/2017/01/11/android-hardware-layer-animation/

当有人问我关于动画性能表现不佳问题时,我首先会问他们是否使用 Hardware layer。

在动画过程中你的视图可能在每一帧都需要进行重绘。如果使用 Hardware layer,可以避免重绘每一帧,因为 View 的渲染一旦进入离屏缓冲区就能够被复用。

另外,Hardware Layer 缓存在 GPU 上,它能够使动画中的某些操作变得更顺畅。通过使用 Hardware Layer 能够迅速渲染简单的变换(如,移动,旋转,缩放,透明度)。因为许多动画都是多种变换的组合,所以使用 Hardware Layer 能够显著提高动画性能。

用法

在使用 Hardware layer 之前,你的视图需要启用硬件加速,可以在 AndroidManifest 文件中的 application 或所在的 activity 加上 android:hardwareAccelerated="true" , 当 Target API >= 14 时,硬件加速是默认启用的。

View 的 layer API 非常简单: View.setLayerType() 。你应该只是暂时设置 Hardware Layer,因为它们无法自动释放(稍后介绍)。基本使用步骤如下:

  • 1, 动画运行前,在每个想要缓存的 View 上调用 View.setLayerType(View.LAYER_TYPE_HARDWARE, null) 。

  • 2, 运行动画

  • 3, 动画结束后,调用 View.setLayerType(View.LAYER_TYPE_NONE, null) 进行清理操作。

示例代码如下:

// Set the layer type to hardware 
myView.setLayerType(View.LAYER_TYPE_HARDWARE, null);

// Setup the animation
ObjectAnimator animator = ObjectAnimator.ofFloat(myView, View.TRANSLATION_X, 150);

// Add a listener that does cleanup 
animator.addListener(new AnimatorListenerAdapter() {  
  @Override
  public void onAnimationEnd(Animator animation) {
    myView.setLayerType(View.LAYER_TYPE_NONE, null);
  }
});

// Start the animation
animator.start();

如果你的 minSdkVersion 16+,并且使用 ViewPropertyAnimator ,可以通过 withLayer() 方法代替上面的操作。

myView.animate()  
  .translationX(150)
  .withLayer()
  .start();

注意事项

。。。你可能猜到了,事情没那么简单。

Hardware Layer 在提升动画性能的能力上表现的十分神奇,如果滥用,将是弊大于利的。切记, 不要盲目使用 Hardware Layer

第一,在某些情况下,实际上 Hardware Layer 可能要做更多的工作,而不仅仅是渲染视图。缓存一个层需要花费时间,因为这一步要划分为两个过程:首先,视图渲染入 GPU 上的一个层中,然后,GPU 再渲染那个层到 Window ,如果 View 的渲染十分简单(比如一个纯色),那么在初始化的时候 Hardware Layer 可能增加额外开销。

第二,对所有缓存来讲,都存在一个缓存失效的可能性。动画运行时,如果某个地方调用了 View.invalidate() ,那么 Layer 就不得不重新渲染一遍。倘若不断地失效,你的 Hardware Layer 实际上要比不添加任何 Layer 性能更差,因为(如上所述)Hardware Layer 在生成缓存的时候增加了开销。如果你不断的重新缓存 Layer ,会对性能造成极大地负担。

这种问题很容易遇到,因为动画通常有多个移动部件。假设你设置了一个拥有三个移动部件的动画。

Parent ViewGroup  
--> Child View 1 (translates left)  
--> Child View 2 (translates right)  
--> Child View 3 (translates up)

如果你仅在父布局 ViewGroup 上设置一个 Layer,实际上会不断地缓存失效,因为(作为一个整体)ViewGroup 会随着子View不断地改变。然而,每个单独的View,仅仅是移动罢了。这种情况下,最好的办法就是在每一个子 View 上设置H ardware Layer(而不是在父布局上)。

在多个View上适当地设置 Hardware Layer,这样他们就不会在动画运行时失效了。

“显示硬件层更新”(Show hardware layers updates) 是追踪这个问题的开发利器。当 View 渲染 Hardware Layer 的时候会闪烁绿色,它应该在动画开始的时候闪烁一次(也就是 Layer 渲染初始化的时候),然而,如果你的View在整个动画期间保持绿色不变,这就是持续的缓存失效问题了。

第三,Hardware Layer 使用 GPU 内存,很明显你不想看到内存泄露。因此,你应该只在必要的时候使用 Hardware Layer,比如,动画运行期间。

所有这一切表明:这里没有硬性规则。Android 渲染系统是复杂的,并且常常令我感到惊讶。如同所有性能问题一样,测试才是关键。“GPU呈现模式分析”和“显示硬件层更新”等开发者选项非常适合确定 Layer 正在帮助还是正在损害你的性能。

示例

原文作者写了个简单的 app,示范 Hardware Layer 的基本用法。 源码在这里

这是运行在 Galaxy Nexus(一款又老又慢的设备)的结果,通过开启开发者选项中的“GPU呈现模式分析”:

[译]通过 Hardware Layer 提升 Android 动画性能

在不使用 Hardware Layer 的情况下,这个简单的动画烂透了。它不断地越过绿线,这就意味着它看起来很糟糕。相比之下,使用 Hardware Layer 的版本一直保持在绿线之下,这非常棒!

第三个例子展示了,在使用 Hardware Layer 的情况下,动画运行时存在缓存失效的风险。由于错误的 Hardware Layer 用法,许多性能收益被干掉了。

(这里有一些奇怪,如果它正处在 invalidating,应该至少与不使用 Hardware Layer 一样迟钝。我不能理解透彻,但是,很显然,即使它们在每一步都必须进行重绘,优化的 Hardware Layer 还是发挥了一些提升性能的作用。所以,最好正确的使用它们。)

这篇文章的寓意是:在合理使用时,Hardware Layer可以很好地提升动画性能!

参考文章

查看原文: http://johnnyshieh.github.io/android/2017/01/11/android-hardware-layer-animation/

共收到0条回复

需要 登录 后回复方可回复, 如果你还没有账号你可以 注册 一个帐号。