Android 3.0(API 11)开始,Android 2D 渲染通道支持硬件加速,这意味着在View的canvas上的执行的所有绘制操作都使用GPU。由于启用硬件加速所需的资源增加,你的应用程序将消耗更多的RAM。
如果您的Target API 级别 >=14,则默认情况下会启用硬件加速,但也可以显式启用硬件加速。如果您的应用程序仅使用标准视图和Drawables,将其全局打开不会引起任何不利的绘图效果。但是,由于不是所有2D绘图操作都支持硬件加速,所以打开它可能会影响您的某些自定义views或绘制的调用。问题通常表现为看不见元素,异常或错误渲染的像素。为了解决这个问题,Android提供了选择,在多个级别启用或禁用硬件加速。
如果您的应用程序执行自定义绘制,请在打开硬件加速的实际硬件设备上测试应用程序,来查看是否有问题。不支持的绘图操作部分描述了硬件加速的已知问题以及如何解决这些问题.
你可以在以下级别控制硬件加速:
在您的Android manifest 文件中,将以下属性添加到
android:hardwareAccelerated="true" ...>
如果您的应用程序在全局开启硬件加速时行为不正常,那么您也可以控制它在单独的activity里。要在Activity级别启用或禁用硬件加速,可以使用android:hardwareAccelerated属性作为
android:hardwareAccelerated="true">
... />
android:hardwareAccelerated="false" />
如果您需要更精准的控制,您可以使用以下代码为指定的Window启用硬件加速:
getWindow().setFlags( WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
Note: 目前无法在Window级别禁用硬件加速
你可以使用以下代码在运行时禁用单个View的硬件加速
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
Note: 无法在View级别启用硬件加速。View层除了禁用硬件加速外,还有其他功能.
有时,对于一个应用知道它是否在硬件加速是有用的,尤其对于自定义view。如果您的应用程序执行大量自定义的绘制并且不是所有的操作都被新的渲染通道很好的支持,这一点尤其重要.
这里有两种方法来检查应用是否支持硬件加速:
View.isHardwareAccelerated()
returns true
如果 View attach 在一个硬件加速的window.Canvas.isHardwareAccelerated()
returns true
如果Canvas是硬件加速的 如果您必须在drawing代码中进行此检查, 请尽可能使用 Canvas.isHardwareAccelerated()
来代替 View.isHardwareAccelerated()
.当view attrach 到硬件加速window时,仍然可以使用非硬件加速的Canvas进行绘制。例如,当将view绘制到bitmap以进行缓存时,会发生这种情况。
当启用硬件加速时,Android框架利用一个新的绘图模型,利用显示列表将你的应用程序呈现在屏幕上。要完全了解显示列表以及它们如何影响你的应用程序,了解Android如何在没有硬件加速的情况下绘制view是非常有用的。以下部分介绍了基于软件和硬件加速的绘图模型.
在软件绘图模型里, views 通过以下2步来绘制:
每当应用需要更新其UI的一部分时,它会在任何已更改内容的view上调用 invalidate() (或等价的).让界面无效的消息一直传播到view层次结构中,以计算需要重绘的屏幕区域(脏区域).然后,Android系统会绘制View层级中所有与脏区域相交的view。不幸的是,这个绘图模型有两个缺点:
Note: 当view属性更改时,Android的views会自动调用 invalidate()
,例如TextView中的背景颜色或文本.
Android系统仍然使用invalidate()和draw()来请求屏幕更新并渲染视图,但是以不同的方式处理实际绘图。 Android系统不需要立即执行绘图命令,而是将其记录在显示列表中,其中包含view层次结构绘制的代码的输出。另一个优化是,Android系统只需要通过invalidate()调用记录和更新标记为dirty的views的显示列表。尚未废用的视图可以通过重新发布先前记录的显示列表来重新绘制。新的绘图模型包含三个阶段:
使用此模型,您不能依赖一个view与脏区域相交来执行其draw()方法.为了确保Android系统记录视图的显示列表,您必须调用invalidate().忘记这样做会导致这个view看起来跟以前一样,甚至在它被改变之后.
使用显示列表也有利于动画性能,因为设置特定的属性(例如Alpha或旋转)不需要使目标view 无效(它会自动完成)。此优化也适用于带有显示列表的view(当应用程序是硬件加速时的任何view。)例如,假设有一个LinearLayout包含一个ListView和一个Button(在ListView下面)。 LinearLayout的显示列表如下所示:
现在假设你想更改ListView的不透明度。在ListView上调用setAlpha(0.5f)后,显示列表现在包含:
ListView
的复杂绘制代码未被执行。相反,系统只更新了更简单的LinearLayout的显示列表。在没有启用硬件加速的应用程序中,列表及其父代的绘制代码将再次被执行.
当硬件加速时,2D渲染管道支持最常用的Canvas绘图操作以及许多较少使用的操作.伴随Android默认的widgets和layouts,常见的高级视觉效果,如反射和平铺纹理,所有的绘制操作都支持渲染应用程序.
下表描述了跨API级别的各种操作的支持级别:
First supported API level | ||||
Canvas | ||||
drawBitmapMesh() (colors array) | 18 | |||
drawPicture() | 23 | |||
drawPosText() | 16 | |||
drawTextOnPath() | 16 | |||
drawVertices() | ✗ | |||
setDrawFilter() | 16 | |||
clipPath() | 18 | |||
clipRegion() | 18 | |||
clipRect(Region.Op.XOR) | 18 | |||
clipRect(Region.Op.Difference) | 18 | |||
clipRect(Region.Op.ReverseDifference) | 18 | |||
clipRect() with rotation/perspective | 18 | |||
Paint | ||||
setAntiAlias() (for text) | 18 | |||
setAntiAlias() (for lines) | 16 | |||
setFilterBitmap() | 17 | |||
setLinearText() | ✗ | |||
setMaskFilter() | ✗ | |||
setPathEffect() (for lines) | ✗ | |||
setRasterizer() | ✗ | |||
setShadowLayer() (other than text) | ✗ | |||
setStrokeCap() (for lines) | 18 | |||
setStrokeCap() (for points) | 19 | |||
setSubpixelText() | ✗ | |||
Xfermode | ||||
PorterDuff.Mode.DARKEN (framebuffer) | ✗ | |||
PorterDuff.Mode.LIGHTEN (framebuffer) | ✗ | |||
PorterDuff.Mode.OVERLAY (framebuffer) | ✗ | |||
Shader | ||||
ComposeShader inside ComposeShader | ✗ | |||
Same type shaders inside ComposeShader | ✗ | |||
Local matrix on ComposeShader | 18 |
首先构建了硬件加速2D渲染管道,以支持未伸缩的绘图,一些绘图操作在较高的尺寸值下显著的降低了质量。这些操作是以伸缩1.0绘制的纹理实现的。在API<17中,使用这些操作将导致缩放产品随尺度而增加.
下表显示了何时更改以正确处理大尺度:Drawing operation to be scaled | First supported API level |
drawText() | 18 |
drawPosText() | ✗ |
drawTextOnPath() | ✗ |
Simple Shapes* | 17 |
Complex Shapes* | ✗ |
drawPath() | ✗ |
Shadow layer | ✗ |
Note: 'Simple' shapes是带有不具有PathEffect的Paint的drawRect(), drawCircle(), drawOval(), drawRoundRect(), and drawArc() (with useCenter=false),并且不包含non-default连接(通过setStrokeJoin()/ setStrokeMiter())。这些绘图命令的其他实例位于上图中的“Complex”下.
如果你的应用因为丢失功能或者限制受到了应系那个,你可以通过调用 setLayerType(View.LAYER_TYPE_SOFTWARE, null)
来关闭应用程序受影响部分的硬件加速.这样,您仍然可以在其他地方使用硬件加速。有关如何在应用程序中启用和禁用不同级别的硬件加速的更多信息,请参阅控制硬件加速.
在Android的所有版本中,view都可以通过使用view的绘图缓存或使用 Canvas.saveLayer()
来呈现离屛缓冲区.离屏缓冲区或图层,有几个用途。您可以使用它们在动画复杂的view或应用组合效果时获得更好的性能。例如,您可以使用 Canvas.saveLayer()
来实现淡入淡出效果,以将视图临时渲染到图层中,然后将其复制到屏幕上,使其具有不透明度因子.
从Android 3.0(API 11)开始,您可以更好地控制如何以及何时使用图层通过 View.setLayerType()方法.此API包含两个参数:要使用的图层类型以及描述图层如何合成的可选Paint对象。您可以使用Paint参数将颜色过滤器,特殊混合模式或不透明度应用于图层。view可选图层类型有三种:
LAYER_TYPE_NONE
: 视图呈现正常,不支持离屛缓冲区。默认选择LAYER_TYPE_HARDWARE
: 如果应用程序是硬件加速的,则view在硬件中呈现为硬件纹理。如果应用程序不是硬件加速,则此层类型的行为与LAYER_TYPE_SOFTWARE相同
.LAYER_TYPE_SOFTWARE
: 该视图通过软件呈现为 bitmap.您使用的图层类型取决于您的目标:
ColorMatrixColorFilter 绘制黑白视图
.当您的应用程序使用硬件加速时,硬件层可以提供更快更平滑的动画. 以60帧/秒的速度运行动画并不总是可行的,尤其是伴随着大量的绘图操作的复杂view.这可以通过使用硬件层将视图呈现到硬件纹理来缓解。然后可以使用硬件纹理来动画化view,从而消除view在动画时不断重绘自身的需要.view 不会重绘除非你改变了view的属性,这个改变会调用 invalidate(),或者你手动调用 invalidate().如果您在应用程序中运行动画,并且不能获得你想要的的平滑过渡的结果,请考虑在动画view上启用硬件层.
当view由硬件层支持时,其某些属性将通过屏幕上合成图层的方式进行处理.设置这些属性将是高效的,因为它们不要求view无效(invalidated)并重新绘制.以下属性列表会影响图层的合成方式.这些属性中的任何一个调用setter可以导致最佳失效并且不重绘目标view:
alpha
: 改变图层的不透明度x
, y
, translationX
, translationY
: 改变图层的位置scaleX
, scaleY
: 改变图层的大小rotation
, rotationX
, rotationY
: 在3D空间中更改图层的方向pivotX
, pivotY
: 改变图层的变换原点 这些属性是使用ObjectAnimator动画化view时使用的名称.如果要访问这些属性,请调用相应的setter或getter.例如,要修改alpha属性,请调用 setAlpha()
. 以下代码片段显示了在3D里围绕Y轴旋转view的最高效方式:
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();
有关属性动画更多的信息,请参阅 Property Animation.
切换到硬件加速的2D图形可以立即提高性能,但你仍然应该通过遵循以下建议设计你的应用程序以有效地使用GPU:
setAlpha()
,
AlphaAnimation
, or
ObjectAnimator
让一个view变得半透明时,它会在离屛缓冲区中呈现,这需要加倍的填充率。在非常大的view上使用Alpha时,请考虑将view的图层类型设置为
LAYER_TYPE_HARDWARE
.