硬件加速

Android 2D 渲染管道支持硬件加速,也就是说,在画布上执行的所有绘制操作都会使用 GPU。启用硬件加速需要更多资源,因此应用会占用更多 RAM。

硬件加速默认处于启用状态。如果您的应用仅使用标准可组合项,则全局启用硬件加速不会造成任何不良绘制效果。不过,并非所有 2D 绘制操作都支持硬件加速,因此启用硬件加速可能会影响您的部分自定义绘制调用。具体问题通常以不可见的元素、异常或错误渲染的像素显现。为了解决此问题,Android 允许您在多个级别选择是启用还是停用硬件加速。请参阅控制硬件加速

如果您的应用执行自定义绘制,请在启用硬件加速的实际硬件设备上测试应用,以检查是否存在任何问题。支持绘制操作部分介绍了已知硬件加速问题和相应的解决方案。

另请参阅通过框架 API 支持 OpenGL

控制硬件加速

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

  • 应用
  • activity
  • 窗口
  • 可组合项

应用级别

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

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

activity 级别

如果您的应用在全局启用硬件加速时无法正常运行,您也可以针对个别 activity 启用或停用硬件加速。如需在 activity 级别启用或停用硬件加速,您可以使用 <activity> 元素的 android:hardwareAccelerated 属性。以下示例展示了如何为整个应用启用硬件加速,但为一个 activity 停用硬件加速:

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

窗口级别

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

window.setFlags(
        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
)

可组合项级别

在 Compose 中,没有可用于停用硬件加速的每个可组合项开关。

如需将可组合项渲染到自己的层中,请使用 Modifier.graphicsLayer。这样一来,转换属性(例如 alphascaleXscaleYtranslationXtranslationYrotationXrotationYrotationZtransformOrigin)就可以在不重新运行可组合函数的绘制代码的情况下发生变化。为获得最佳性能,请始终使用修饰符的 lambda 形式来设置这些属性。

如需明确强制使用屏幕外缓冲区以进行高级绘制操作(例如图层内的自定义混合),请使用 CompositingStrategy.Offscreen。如需了解详情,请参阅图形修饰符

如果您有严格要求使用软件渲染的自定义绘制操作,可以使用 AndroidView 托管旧版 View,并对该视图调用 setLayerType(View.LAYER_TYPE_SOFTWARE, null)

支持绘制操作

经过硬件加速后,2D 渲染管道支持最常用的 Canvas 绘制操作以及很多不太常用的操作。用于渲染 Android 系统内置应用、标准可组合项以及常见的高级视觉效果(例如反射和平铺纹理)的所有绘制操作均受到支持。

下表介绍了各种操作在各个 API 级别的支持级别:

第一个支持的 API 级别
画布
drawBitmapMesh()(颜色数组) 18
drawPicture() 23
drawPosText() 16
drawTextOnPath() 16
drawVertices() 29
setDrawFilter() 16
clipPath() 18
clipRegion() 18
clipRect(Region.Op.XOR) 18
clipRect(Region.Op.Difference) 18
clipRect(Region.Op.ReverseDifference) 18
clipRect()(通过旋转/透视) 18
绘制
setAntiAlias()(适用于文本) 18
setAntiAlias()(适用于线条) 16
setFilterBitmap() 17
setLinearText()
setMaskFilter()
setPathEffect()(适用于线条) 28
setShadowLayer()(除文本之外) 28
setStrokeCap()(适用于线条) 18
setStrokeCap()(适用于点) 19
setSubpixelText() 28
Xfermode
PorterDuff.Mode.DARKEN(帧缓冲区) 28
PorterDuff.Mode.LIGHTEN(帧缓冲区) 28
PorterDuff.Mode.OVERLAY(帧缓冲区) 28
着色器
ComposeShader 内的 ComposeShader 28
ComposeShader 内相同类型的着色器 28
ComposeShader 上的本地矩阵 18

画布缩放

硬件加速 2D 渲染管道最初是为了支持不可缩放的绘制构建的,其中一些绘制操作会以较高的缩放值显著降低质量。这些操作实现为按 1.0 的缩放值绘制的纹理,由 GPU 进行转换。从 API 级别 28 开始,所有绘制操作都可以顺利缩放。

下表列出了何时更改实现以正确处理大规模缩放:

要缩放的绘制操作 第一个支持的 API 级别
drawText() 18
drawPosText() 28
drawTextOnPath() 28
简单形状 17
复杂形状 28
drawPath() 28
阴影层 28

如果您依赖的绘制操作未进行硬件加速,请将受影响的绘制渲染到屏幕外软件 Bitmap(或 ImageBitmap)中,然后绘制结果。其余界面会保留硬件加速路径。

提示和技巧

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

最大限度地降低布局复杂性和重新组合
保持布局树较浅,并限制重新组合的次数。将状态读取推迟到尽可能小的范围内,以便更改仅重绘尽可能小的区域。例如,在 Modifier.graphicsLayer { } 内读取动画状态,而不是在可组合项的正文中读取。如需了解详情,请参阅 Jetpack Compose 性能
避免过度绘制
请勿在彼此上方绘制过多层。移除所有被上方的其他不透明元素完全遮挡的界面元素。如果您需要在彼此上方混合绘制多个层,请考虑将它们合并为一个层。对于目前的硬件来说,绘制的层数最好不超过屏幕上每帧像素数的 2.5 倍(透明像素,以位图计数!)。
请勿在绘制方法中创建渲染对象
一个常见的错误是,每次调用渲染方法时都创建新的 PaintPath。这会强制垃圾回收器更频繁地运行,同时还会绕过硬件管道中的缓存和优化。为避免这种情况,请重用并更改对象:
  • 使用标准方法:标准 DrawScope 方法(如 drawRectdrawCircle)已在内部重复使用 Paint 对象,无需开发者分配。
  • 进行突变而非重新分配:在编写自定义逻辑时,请使用 path.rewind 清除现有 Path,而不是实例化新的 Path
  • 高效保持状态:在可组合函数内,使用 remember { Path() } 分配对象一次。如果您要构建可重用的自定义修饰符扩展程序,请使用 DrawModifierNode 实现自定义 Modifier.Node,以分配和重用对象,而不会导致新的堆分配。
请勿过于频繁地修改形状
例如,使用纹理遮罩渲染复杂的形状、路径和圆圈。每次创建或修改路径时,硬件流水线都会创建新的遮罩,成本可能比较高。
请勿过于频繁地修改位图
每次更改位图的内容时,系统都会在您下次绘制时将其作为 GPU 纹理再次上传。
谨慎使用 Alpha
当您使用 Modifier.alpha 或 Compose 动画 API 将可组合项设置为半透明时,该可组合项通常会在屏幕外缓冲区渲染,导致所需的填充率翻倍。为避免非重叠内容的屏幕外缓冲区开销,请设置 CompositingStrategy.ModulateAlpha。对于单个绘制调用,请直接将 Alpha 应用于绘制命令(例如使用 color = Color.Red.copy(alpha = 0.5f)),而无需创建图层。

其他资源

查看内容