比较 Compose 指标和 View 指标

Jetpack Compose 可加快界面开发并改进 Android 开发。不过,请考虑向现有应用添加 Compose 可能会对应用的 APK 大小、构建和运行时性能等指标产生怎样的影响。

APK 大小和构建时间

本部分将通过 Sunflower 示例应用(该应用演示了将基于 View 的应用迁移到 Compose 的最佳实践)来介绍对 APK 大小和构建时间的影响。

APK 大小

将库添加到项目中会增加其 APK 大小。以下是 Compose 对每个项目的缩减版 APK 的影响,这些项目启用了资源和代码缩减功能,使用 R8 完整模式。影响的结果使用 APK 分析器进行衡量。

仅查看 混合使用 View 和 Compose 仅限 Compose
下载大小 2,252 KB 3,034 KB 2,966 KB

首次将 Compose 添加到 Sunflower 时,APK 大小从 2,252 KB 增加到 3,034 KB,增加了 782 KB。生成的 APK 包含混合了 View 和 Compose 的界面 build。由于向 Sunflower 添加了其他依赖项,因此出现这种增加是意料之中的。

相反,将 Sunflower 迁移为仅使用 Compose 的应用后,APK 大小从 3,034 KB 减少到 2,966 KB,减少了 68 KB。之所以减少,是因为移除了未使用的 View 依赖项,例如 AppCompatConstraintLayout

构建时间

添加 Compose 会增加应用的构建时间,因为 Compose 编译器会处理应用中的可组合项。以下结果是使用独立的 gradle-profiler 工具获得的,该工具会多次执行构建,以便为 Sunflower 的调试 build 时长获取平均构建时间:

gradle-profiler --benchmark --project-dir . :app:assembleDebug
仅查看 混合使用 View 和 Compose 仅限 Compose
平均构建时间 299.47 毫秒 399.09 毫秒 342.16 毫秒

首次将 Compose 添加到 Sunflower 时,平均构建时间从 299 毫秒增加到 399 毫秒,增加了 100 毫秒。这是因为 Compose 编译器会执行其他任务来转换项目中定义的 Compose 代码。

相反,在完成 Sunflower 向 Compose 的迁移后,平均构建时间缩短至 342 毫秒,减少了 57 毫秒。构建时间缩短的原因在于多种因素共同发挥作用,这些因素共同缩短了构建时间,例如移除数据绑定、将使用 kapt 的依赖项迁移到 KSP,以及将多个依赖项更新到其最新版本。

摘要

由于 Compose 代码的编译过程,采用 Compose 会有效增加应用的 APK 大小,并增加应用的构建时间性能。不过,这些权衡需要与 Compose 的好处进行权衡,尤其是在采用 Compose 后开发者工作效率提高方面。例如,Play 商店团队发现,编写界面所需的代码量要少得多,有时甚至可减少 50%,从而提高了代码的生产力和可维护性。

如需阅读更多案例研究,请参阅采用 Compose for Teams

运行时性能

本部分介绍了与 Jetpack Compose 中的运行时性能相关的主题,以帮助您了解与 View 系统相比,Jetpack Compose 的性能如何,以及如何衡量性能。

智能重组

如果界面的某些部分无效,Compose 会尝试只重组需要更新的部分。如需详细了解,请参阅可组合项的生命周期Jetpack Compose 阶段文档。

基准配置文件

基准配置文件是加快常见用户体验历程的绝佳方式。在应用中添加基准配置文件可以避免对包含的代码路径执行解译和即时 (JIT) 编译步骤,从而让代码执行速度从首次启动开始提高约 30%。

Jetpack Compose 库包含自己的基准配置文件,当您在应用中使用 Compose 时,系统会自动获取这些优化。不过,这些优化仅会影响 Compose 库内的代码路径,因此我们建议您向应用添加基准配置文件,以涵盖 Compose 之外的代码路径。

与 View 系统比较

与 View 系统相比,Jetpack Compose 进行了许多改进。以下部分介绍了这些改进。

对 View 进行了全方位扩展

在屏幕上绘制的每个 View(例如 TextViewButtonImageView)都需要内存分配、显式状态跟踪和各种回调,才能支持所有用例。此外,自定义 View 所有者需要实现显式逻辑,以防止在非必要情况下(例如,重复数据处理)重新绘制。

Jetpack Compose 通过多种方式解决了此问题。Compose 中没有用于绘制 View 的显式可更新对象。界面元素是简单的可组合函数,这些函数的信息以可重放的方式写入组合。这有助于将显式状态跟踪、内存分配以及回调缩减为仅对需要上述功能的可组合项执行,而不是对给定 View 类型的所有扩展都执行。

此外,Compose 还提供了智能重组功能,可在无需进行任何更改的情况下重放之前绘制的结果。

多次布局传递

传统的 ViewGroup 在性能衡量 API 和布局 API 方面有诸多表现形式,因此很容易导致发生多次布局传递。如果在视图层次结构中的特定嵌套点进行了多次布局传递,可能会导致工作量呈指数级增长。

Jetpack Compose 通过其 API 协定为所有布局可组合项强制执行单次布局传递。这使 Compose 能够高效地处理较深的界面树。如果需要进行多次测量,Compose 具有固有特性测量

View 启动性能

首次显示特定布局时,View 系统需要膨胀 XML 布局,Jetpack Compose 则节省了此开销,因为布局是使用 Kotlin 编写的,并像应用的其余部分一样进行编译。

对 Compose 进行基准测试

在 Jetpack Compose 1.0 中,应用在 debugrelease 模式下的性能存在显著差异。为使得出的时间结果具有代表性,在对应用进行性能分析时,请始终使用 release build(而非 debug)。

如需了解 Jetpack Compose 代码的性能,您可以使用 Jetpack Macrobenchmark 库。如需了解如何将其与 Jetpack Compose 搭配使用,请参阅 MacrobenchmarkSample 项目

Jetpack Compose 团队还使用 Macrobenchmark 来捕获可能发生的任何回归问题。例如,请查看 lazy 列的基准及其信息中心,以跟踪回归问题。

Compose 配置文件安装

由于 Jetpack Compose 是未捆绑库,因此它无法受益于 Zygote,后者会预加载 View 系统的界面工具包类和可绘制对象。Jetpack Compose 1.0 利用了 release build 的配置文件安装。ProfileInstaller 可让应用指定要在安装时进行预编译 (AOT) 的关键代码。Compose 随附配置文件安装规则,可减少 Compose 应用的启动时间和卡顿。