图片和图形之硬件加速(7)

原文

概要


从Android 3.0(API级别11)开始,Android 2D渲染管道支持硬件加速,这意味着在View画布上执行的所有绘图操作都 使用GPU。由于启用硬件加速所需的资源增加,您的应用程序将消耗更多的RAM。

如果您的目标API级别大于等于14,但也可以显式启用,则默认情况下会启用硬件加速。如果您的应用程序仅使用标准视图和 Drawables,则全局打开它不应导致任何不利的绘图效果。但是,由于所有2D绘图操作都不支持硬件加速,因此将其打开可能会影响某些自定义视图或绘图调用。问题通常表现为不可见的元素,例外或错误渲染的像素。为了解决这个问题,Android允许您在多个级别启用或禁用硬件加速。请参阅控制硬件加速。

如果您的应用程序执行自定义绘图,请在开启硬件加速功能的实际硬件设备上测试您的应用程序以发现任何问题 在不支持的绘图操作部分介绍了硬件加速,以及如何解决它们的已知问题。

另请参阅使用框架API 和Renderscript的OpenGL

控制硬件加速


您可以在以下级别控制硬件加速:

  • Application
  • Activity
  • Window
  • View

Application level
在Android清单文件中,将以下属性添加到 标记中,以便为整个应用程序启用硬件加速: <application>

<application android:hardwareAccelerated="true" ...>

Activity level

如果您的应用程序在全球范围内打开硬件加速时无法正常运行,您也可以控制它以进行单独的活动。要在活动级别启用或禁用硬件加速,可以使用android:hardwareAccelerated该元素的属性。以下示例为整个应用程序启用硬件加速,但为一个活动禁用它: <activity>

<application android:hardwareAccelerated="true">
    <activity ... />
    <activity android:hardwareAccelerated="false" />
</application>

Window level
如果您需要更细致的控制,则可以使用以下代码为给定窗口启用硬件加速:

getWindow().setFlags(
    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
注意:您目前无法在窗口级别禁用硬件加速。

View level
您可以在运行时使用以下代码禁用单个视图的硬件加速:

myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
注意:您目前无法在视图级别启用硬件加速。除禁用硬件加速外,视图图层还具有其他功能。有关其用途的更多信息,请参阅[查看图层](https://developer.android.com/guide/topics/graphics/hardware-accel#layers)。

确定视图是否是硬件加速的


对于一个应用程序来说,它是否有助于了解它当前是否是硬件加速的,特别是对于诸如自定义视图之类的东西。如果您的应用程序执行了大量的自定义绘制,并且并非所有操作都得到了新渲染管道的正确支持,那么这非常有用。

有两种不同的方法来检查应用程序是否是硬件加速的:

  • View.isHardwareAccelerated()true如果View连接到硬件加速窗口,则返回 。

  • Canvas.isHardwareAccelerated()true如果Canvas硬件加速则 返回

如果您必须执行此操作,请检查您的绘图代码, Canvas.isHardwareAccelerated()而不是View.isHardwareAccelerated()在可能的情况下使用。当视图附加到硬件加速窗口时,仍然可以使用非硬件加速画布绘制视图。例如,为了缓存目的,将视图绘制到位图时会发生这种情况。

Android绘图模型


当启用硬件加速时,Android框架将利用新的绘图模型,利用显示列表将应用程序呈现在屏幕上。要充分了解显示列表以及它们可能如何影响您的应用程序,了解Android如何在没有硬件加速的情况下绘制视图也很有用。以下各节介绍基于软件和硬件加速的绘图模型。

基于软件的绘图模型

在软件绘图模型中,视图通过以下两个步骤绘制:

  • 使层次结构失效
  • 绘制层次结构

每当应用程序需要更新其UI的一部分时,它就会invalidate()在任何已更改内容的视图上调用(或其中一种变体)。无效消息一直传播到视图层次结构中,以计算需要重绘的屏幕区域(脏区域)。Android系统然后在与脏区相交的层级中绘制任何视图。不幸的是,这个绘图模型有两个缺点:

* 首先,这个模型需要在每个绘制过程中执行大量的代码。例如,如果您的应用程序调用invalidate()某个按钮,并且该按钮位于另一个视图的顶部,则Android系统即使没有更改也会重新绘制该视图。

* 第二个问题是绘图模型可以隐藏应用程序中的错误。由于Android系统在与脏区域相交的情况下重绘视图,因此您更改的内容的视图可能会重新绘制,即使invalidate()未对其调用也是如此。发生这种情况时,您正在依靠另一个视图被无效以获得正确的行为。每次修改应用程序时,此行为都会更改。因此,invalidate()无论何时修改影响视图的绘图代码的数据或状态,都应该始终调用自定义视图。

注意:Android视图会invalidate()在其属性发生更改时自动调用,例如背景颜色或a中的文本TextView。

硬件加速绘图模型

Android系统仍然使用invalidate()并draw()请求屏幕更新并呈现视图,但以不同的方式处理实际的图形。Android系统不是立即执行绘图命令,而是将它们记录在显示列表中,显示列表包含视图层次结构绘图代码的输出。另一个优化是Android系统只需要记录和更新显示列表,以查看通过invalidate() 调用标记为脏的视图。没有失效的视图可以通过重新发布之前记录的显示列表重新绘制。新的绘图模型包含三个阶段:

  1. Invalidate the hierarchy:使层次结构失效
  2. Record and update display lists:记录和更新显示列表
  3. Draw the display lists:绘制显示列表

有了这个模型,就不能依赖与脏区域相交的视图来draw()执行其方法。为确保Android系统记录视图的显示列表,您必须调用invalidate()。忘记这样做会导致视图在更改后看起来一样。

使用显示列表也有利于动画性能,因为设置特定的属性(如alpha或旋转)不需要使目标视图无效(它会自动完成)。此优化还适用于带有显示列表的视图(应用程序硬件加速时的任何视图)。例如,假设有一个LinearLayout包含ListView上面的a Button。显示列表LinearLayout如下所示:

  • DrawDisplayList(ListView)
  • DrawDisplayList(Button)

现在假设你想改变ListView不透明度。调用后setAlpha(0.5f)的ListView,显示列表现在包含这样的:

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

复杂的绘图代码ListView没有执行。相反,系统只更新了更简单的显示列表LinearLayout。在未启用硬件加速的应用程序中,列表及其父项的绘图代码将再次执行。

不支持的绘图操作

当硬件加速时,2D渲染管线支持最常用的 Canvas绘图操作以及许多较少使用的操作。支持所有用于渲染Android应用程序的绘图操作,默认窗口小部件和布局以及反射和平铺纹理等常见高级视觉效果。

下表介绍了各个API级别的各种操作的支持级别:
图片和图形之硬件加速(7)

画布缩放

硬件加速的二维渲染流水线首先被构建以支持未缩放的绘图,一些绘图操作在较高刻度值时显着降低质量。这些操作实现为以1.0比例绘制的纹理,由GPU转换。在API级别<17中,使用这些操作会导致缩放工件随着比例增加。

下表显示何时更改实施以正确处理大比例:
图片和图形之硬件加速(7)

注:“简单”形状drawRect(), drawCircle(),drawOval(),drawRoundRect(),和 drawArc()(同useCenter = FALSE)与不具有PathEffect,并且不包含非默认连接(通过涂料发出的命令setStrokeJoin()/ setStrokeMiter())。在上面的图表中,这些绘图命令的其他实例属于“复杂”。

查看图层

在Android的所有版本中,视图都可以通过使用视图的绘图缓存或通过使用来渲染到屏幕外缓冲区Canvas.saveLayer()。离屏缓冲区或图层有几种用途。动画处理复杂视图或应用合成效果时,您可以使用它们获得更好的性能。例如,您可以使用Canvas.saveLayer()临时渲染视图到一个图层并使用不透明度因子将其复合到屏幕上来实现淡入淡出效果。

从Android 3.0(API级别11)开始,您可以更好地控制如何以及何时在View.setLayerType()方法中使用图层。该API采用两个参数:要使用的图层类型和Paint 描述应该如何合成图层的可选对象。您可以使用该Paint参数将颜色过滤器,特殊混合模式或不透明度应用于图层。视图可以使用三种图层类型之一:

  • LAYER_TYPE_NONE:视图呈现正常,并且不被屏幕外缓冲区支持。这是默认行为。
  • LAYER_TYPE_HARDWARE:如果应用程序是硬件加速的,则该视图在硬件中呈现为硬件纹理。如果应用程序不是硬件加速的,则此图层类型的行为与LAYER_TYPE_SOFTWARE。
  • LAYER_TYPE_SOFTWARE:视图以软件呈现为位图。

您使用的图层类型取决于您的目标:

  • 性能:使用硬件图层类型将视图渲染为硬件纹理。视图渲染到图层后,视图调用之前不需要执行其绘图代码invalidate()。然后可以将一些动画(如alpha动画)直接应用到图层上,这对于GPU来说非常有效。
  • 视觉效果:使用硬件或软件图层类型,并对Paint视图应用特殊视觉处理。例如,您可以使用a绘制黑白视图ColorMatrixColorFilter。
  • 兼容性:使用软件层类型强制视图以软件呈现。如果硬件加速的视图(例如,如果整个应用程序硬件加速),则会导致渲染问题,这是解决硬件渲染管道限制的简单方法。

查看图层和动画

当您的应用程序硬件加速时,硬件层可以提供更快更流畅的动画。在动画处理发生大量绘图操作的复杂视图时,以每秒60帧的速度运行动画并不总是可行。这可以通过使用硬件层将视图渲染为硬件纹理来缓解。然后可以使用硬件纹理来为视图设置动画,从而消除视图在动画时不断重新绘制自身的需要。除非您更改视图的属性,调用invalidate()或invalidate()手动调用,否则不会重新绘制视图。如果您在应用程序中运行动画并且没有获得您想要的平滑结果,请考虑在动画视图上启用硬件层。

当视图由硬件层支持时,其某些属性由图层在屏幕上合成的方式处理。设置这些属性将很有效,因为它们不要求视图失效并重绘。以下属性列表会影响图层合成的方式。为这些属性调用setter会导致最佳的失效并且不会重新绘制目标视图:

  • alpha:更改图层的不透明度
  • x,y,translationX,translationY:更改层的位置
  • scaleX,scaleY:更改图层的大小
  • rotation,rotationX,rotationY:改变层的三维空间定位
  • pivotX,pivotY:更改图层的转换原点

这些属性是使用动画视图时使用的名称ObjectAnimator。如果你想访问这些属性,请调用适当的setter或getter。例如,要修改alpha属性,请调用setAlpha()。以下代码片断展示了围绕Y轴旋转3D视图的最有效方法:

view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator.ofFloat(view, "rotationY", 180).start();

由于硬件层使用视频内存,强烈建议您只在动画期间启用它们,然后在动画完成后禁用它们。您可以使用动画侦听器完成此操作:

view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotationY", 180);
animator.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        view.setLayerType(View.LAYER_TYPE_NONE, null);
    }
});
animator.start();

有关属性动画的更多信息,请参阅属性动画

技巧和窍门


切换到硬件加速的2D图形可以立即提高性能,但您仍应该按照以下建议设计应用程序以有效使用GPU:

减少应用程序中的视图数量

系统绘制的视图越多,速度就越慢。这也适用于软件渲染管道。减少视图是优化UI的最简单方法之一。

避免透支

不要在彼此顶部绘制太多的图层。删除任何完全被其他不透明视图遮挡的视图。如果您需要绘制混合在一起的多个图层,请考虑将它们合并到一个图层中。使用当前硬件的一个很好的经验法则是每帧画面的像素数不会超过2.5倍(位图计数中的透明像素!)。

不要在绘制方法中创建渲染对象

一个常见的错误是每次创建一个新的Paint或新Path的渲染方法被调用。这迫使垃圾收集器更频繁地运行,并绕过硬件管道中的缓存和优化。

不要经常修改形状

实例中的复杂形状,路径和圆圈使用纹理蒙版进行渲染。每次创建或修改路径时,硬件管道都会创建一个新的掩码,这可能很昂贵。

不要经常修改位图

每次更改位图的内容时,下次绘制时都会再次将其作为GPU纹理上传。

小心使用阿尔法

当您使用setAlpha(),, AlphaAnimation或者使视图变得半透明时ObjectAnimator,它将呈现在屏幕缓冲区中,该缓冲区将所需填充率加倍。在非常大的视图上应用alpha时,请考虑将视图的图层类型设置为 LAYER_TYPE_HARDWARE。

联系我

QQ:94297366
微信打赏:https://pan.baidu.com/s/1dSBXk3eFZu3mAMkw3xu9KQ

公众号推荐:

图片和图形之硬件加速(7)

猜你喜欢

转载自blog.51cto.com/4789781/2120402