硬件加速

硬件加速的问题

由于硬件加速不支持某些2D绘制操作,因此可能会导致某些问题。表现为:元素不可见、异常、某些像素错误渲染。

但是,View可以改变View Layer的渲染方式。

setLayerType()的作用(改变Layer的渲染方式)

View可以渲染离屏buffer。方式有两种,绘制cache和使用Canvas.saveLayer();
离屏buffer,也被称为layer,其作用是:
在渲染负杂view的时候或执行组合效果的时候提供更好的性能。
比如可以使用Canvas.saveLayer()来临时将一个view渲染到layer里面,然后为它设置一个透明值,再组合回屏幕。以这种方式来实现fade效果。

从Android 3.0开始,可以通过View.setLayerType()来控制layer的执行方式。第一个参数是layer类型,比如是否启用硬件加速。第二个参数是Paint类型,指定l*ayer被组合的方式*,比如用这个来执行滤色、混合或者透明等。

layer类型有三种:

  • LAYER_TYPE_NONE: 默认行为,不支持离屏缓冲。
  • LAYER_TYPE_HARDWARE: view通过硬件渲染,渲染为一个纹理。必须要application的硬件加速开启。如果没有开启,则将会采用LAYER_TYPE_SOFTWARE的行为。
  • LAYER_TYPE_SOFTWARE: View被软件渲染成位图。

不同类型的渲染方式可以达到不同的目的:

  • 性能: 使用硬件加速会将view渲染成GPU所使用的纹理。渲染完成后,只有当调用invalidate()才会执行绘制代码。一些动画,比如透明动画,可以直接应用在layer上,GPU处理这类型的计算非常快。
  • 视觉效果: 通过执行Paint效果,可以实现各种视觉效果。
  • 兼容性: 使用软件渲染可以解决硬件渲染的某些兼容性问题。

硬件加速的layer渲染完成之后(被渲染成纹理),如果不调用invalidate(),是不会重新渲染的,因此也就可以大大提高绘制速度。并且,在这种layer上改变某些属性也不会导致重新渲染:

  • alpha透明度
  • x, y ,translationX, translationY
  • sacleX, scaleY
  • rotation
  • pivotX, pivotY

Android绘制模式

有两种绘制模式:

软件绘制模式

这种模式下,View的绘制分为两步:
1. InvalidateView树
2. 绘制视图树

当需要更新一部分UI时,在发生改变的view上调用invalidate()以及其变种。这个消息会沿着视图树传递上去,视图树会计算出屏幕上哪些地方需要重绘(脏区)。系统将会重绘视图树上与脏区交界的view。这个模型有两个缺点:

  • 每一个绘制流程都需要执行大量的代码。比如,如果你在一个Button上调用invalidate(),并且这个Button在其他View的顶部,系统会重新绘制这些View,即使它们没有发生改变。
  • 第二个缺点是这种模式可能会隐藏一些bug在你的应用中。由于系统会重绘与脏区交界的View,因此View可能会在没有调用invalidate()的情况下被重绘。这种情况下,你就要依靠另一个View去invaliated来产生正确的行为。每次修改应用程序的时候都有可能改变这种行为,由于这个原因,当你修改了某些数据或者状态会影响到View的绘制时,你就必须每次都要在你的自定义View上调用invalidate()

注意:Android View会在属性改变的时候自动调用invalidate()。比如,在一个TextView中,它的background color和text被改变时。

硬件加速模式

这种模式下,系统仍然使用invalidate()draw()来请求屏幕更新和渲染View,但是在实际的绘制上采用不同的方式。
不同于之前立即执行绘制命令,Android系统会记录一个内部的display list,它包含了视图树绘制代码。另一方面,系统只需要对每个在invalidate()调用中被标记为脏区的view,记录并更新display list。没有被invalidate的View只需要重用之前的display list来重新绘制就好了。这种新模式下,有三个步骤:

  1. invalidate视图树
  2. 记录并更新display lists
  3. 绘制display lisdts

在这种模式下,你就不能依靠view与脏区交界的机制来执行draw()方法。你必须调用invalidate()来确保系统记录了你的view的display list。如果不这样做,即使是View已经改变了,你看到的还是原先那个View。

使用display list同样对性能提升有益,因为设置某些属性,比如alpha和rotation等,不需要请求invalidate目标view(系统会自动处理)。这个优化同样被应用到拥有display lists的View上(需要application开启硬件加速)。比如假设有一个LineLayout,包含了一个Button,在Button上方有一个ListView。这个LinearLayout的display list可以简单描述为:

  • DrawDisplayList(ListView)
  • DrawDisplayList(Button)

假设你现在想改变ListView的透明度。在对ListView执行了`setAlpha(0.5f)后,display list现在看起来是这样:

  • SaveLayerAlpha(0.5)
  • DrawDisplayList(ListView)
  • Restore
  • DrawDisplayList(Button)

ListView复杂的绘制代码并没有被执行。相反,系统仅仅是简单地更新了LinearLayout。在没有启用硬件加速时,list和layout的绘制代码都会被执行一次。


总结

我还查阅了stackoverflow上的一些相关的问题。最后总结如下:

canvas是否硬件加速并不仅仅取决于View的设置,还与View的类型有关。
而View的setLayerType()的作用也并不是简单地影响canvas的渲染类型 。

你可能感兴趣的:(Android)