Jetpack Compose 使用前后对比

Jetpack Compose 使用前后对比

为了包含 Jetpack Compose 1.0.0-beta05 的更新内容,这篇文章在第一次发布后做出了更新。如果您希望查看 原始版本,请点击 这里

2020 年,我开始了缓慢迁移 Tivi UI 的任务,目标是使其转为由 Jetpack Compose 编写。大约 12 个月之后,任务完成!🎉

在本文中,我们将会回顾并对比一些关键指标,以了解 Compose 相对的优势,其中包括: APK 大小、构建速度、代码行数。

应用本身

在我们进一步了解 Compose 的相关内容前,先让我快速地描述一下应用本身。

Tivi 已经高度模块化,它每个 UI 的界面都在其自身的 Gradle 模块中 (名为 ui-$NAME)。每个界面都使用 Fragment 实现,随后在主 app 模块中使用 AndroidX Navigation 将它们结合起来。为了让您对架构有一个直观印象,下面是应用的模块图:

△ Tivi 的模块图,使用 Jake Wharton 所提供的,十分方便的 Gradle 任务 生成

由于导航图使用 深度链接 URI 实现,大多数 Fragment 彼此之间一无所知,从而保证了解耦。或许更为重要的是,这种形式支持独立模块编译,从而有助于进行并行构建。

注意: Tivi 的模块结构并不完美。其中,UI 模块 (位于顶层) 与基础模块 (位于底层) 间仍有许多依赖。理想情况下,每个层级应当保持分离。我接下来的工作正是要优化这一问题。

在开始迁移至 Compose 之前,Tivi 已经使用了 Android 开发者可以使用的所有炫酷 UI 组件,包括但不限于: Data BindingEpoxyMaterial Design ComponentsInsetter DBXMotionLayout。但不幸的是,其中的许多组件使用了注解处理,因而也带来了额外的构建消耗。

迁移流程

前面我曾提到,我们已经完成了迁移的 "第一幕",这话是什么意思呢?现在的应用看起来与我在 2020 年二月开始迁移时别无二致。

应用的模块化意味它可以分片完成迁移,每次只用迁移一个 Fragment——而这也是近 11 个月以来 46 个拉取请求 中所做的事。

我从一个简单的界面: 剧集详情 开始迁移,接下来是 演出详情、"发现"、"搜索"、"关注的演出" 等等。最近在 Paging3 支持了 Compose 后,我迁移了最后的界面: "列表" 网格:

△ 迁移前后,Tivi 中展示视频的效果

△ 迁移前后,Tivi 中展示视频的效果

应用迁移的第一个阶段使用了 FragmentsNavigation,同时每个 Fragment 的 UI 使用了 Jetpack Compose 实现。

第二个 (也是最后一个) 阶段是从 Fragment 迁出,并直接使用 Navigation Compose 组件。这一步在 这个 PR 中完成。

迁移的过程对我来说轻而易举,毫无疑问 Compose 便是 Android UI 开发的未来。

下面,让我们看看具体指标… 📊

指标

针对下列的每一个指标,我们都对比了应用的三个不同版本:

  1. 接入 Compose 前 : 回到 2020 年 2 月,这是我为 Tivi 添加 Comepse 支持的第一个 PR 发布之前提交
  2. Fragments + Compose : 此版本所基于的 提交,被标记为迁移第一阶段的结尾。我检出了新的分支,并将 Jetpack Compose 更新到 1.0.0-beta05、AGP 更新到 7.0.0-alpha14、Gradle 更新到 7.0 以及 Kotlin 更新到 1.4.32,以减少比较中的不确定因素。您可以在 本分支 中看到相关的代码。
  3. 完全接入 Compose : 这一版本使用了当前最新的主 提交。此时的 Tivi 已经完全基于 Compose (版本 1.0.0-beta05) 了,同时在整个应用中都没有 Fragment。

APK 尺寸缩减 🗜

您的用户最为关心的指标,莫过于 APK 大小。

下面是开启了 资源缩减 的最小化发布版 APK (使用了 R8) 通过 APK Analyzer 所测量的结果:

△ 展示 Tivi APK 大小的图表

△ 展示 Tivi APK 大小的图表

△ 展示 Tivi 方法数的图表

△ 展示 Tivi 方法数的图表

关于上述数字的说明:

  • 我们使用了 APK Analyzer 报告的 "APK file size" (而不是下载时的大小)。

APK 大小分析

在将迁移后的应用与接入 Compose 前的应用做比较后,我们发现 APK 大小缩减了 41%,方法数减少了 17%

在使用了 Compose 后,我们发现 APK 大小缩减了 41%,方法数减少了 17%

这一数字表明,当您需要保留所有 View 类,以防出现需要在布局文件中使用它们的情况时,压缩工具的作用十分有限。

代码行数 📜

我知道在比较软件项目时,计算源代码行数不是特别有用的统计方式;但这种方式能够提供一个视角,帮助我们了解事物是如何变化的。

为了进行测试,我使用了 cloc 工具。使用下面的命令可以排除各种构建文件、自动生成文件以及配置文件。

cloc . --exclude-dir=build,.idea,schemas

△ 展示 Tivi 源代码行数的图表

△ 展示 Tivi 源代码行数的图表

cloc 内建支持忽略注释的功能 (虽然我没有进行验证),因此上面的结果适用于实际的 "代码"。毫不意外的,XML 行数大幅减少了 76%。再见了,布局文件,以及 styles、theme 等其他的 XML 文件👋。

有趣的是,Kotlin 代码的总行数也下降了。我对此现象的理解是,现在应用中的模板代码减少了,同时我们也得以移除大量的视图辅助类和工具类代码。您可以看到,我在 这个 PR 中删除了多年来编写的近 3,000 行代码。

构建速度 ⏳

构建速度是开发者们十分关心的一项指标。在开始处理之前,我觉得移除大量的注解处理器有助于提升构建速度,但我不确定能提升多少。

测试设置

在进行下一步前,很重要的一点是要知道我是如何测量出下面的数字的。我遵循了与 Chris Horner 测量 不同 CPU 上的构建时间 时类似的设置。

我在一台 Lenovo P920 上进行测试,它拥有 192GB RAM 和速度极快的 Xeon Gold 6154 CPU。不用多说,我知道这台机器不是开发者的通常配置,所以为了使测试尽量逼真,我将 CPU 固定在了其最小的时钟频率上:

# 使用调频器的 performance 调速器来更改最大运行频率
sudo cpupower frequency-set -g performance
# 将最大频率设定为 CPU 的最低值:1.2GHz
sudo cpupower frequency-set -u 1.2GHz

为了准备所有的远程缓存文件,接下来我运行了 ./gradlew assembleDebug

为了执行测试,我循环运行了下列命令五遍:

./gradlew --profile \
          --offline \
          --rerun-tasks \
          --max-workers=4 \
          assembleDebug

这里并不一定需要配置 --max-workers,但这里如果不设置,Gradle 会使用该 CPU 默认可用的所有 64 个核心。限制到 4 个更接近典型的笔记本电脑 CPU。

结果

您可以在下面看到结果,每个结果都使用了结果报告中的 "总构建时间 (Total Build Time)" 值。

△ 展示 Tivi 构建时间中位数的图表

△ 展示 Tivi 构建时间中位数的图表

这一结果令我有些惊奇,因为 "完全接入 Compose" 比 "在每个 Fragment 中使用 Compose" 快了 25 秒

感谢 Ivan Gavrilović 找出原因。这一现象与 Compose 无关。"完全接入 Compose" 使用的是最新版本的 Dagger/Hilt,该版本使用了 Android Gradle Plugin 7.0 中的新 ASM API。而其他版本使用了较旧的 Hilt 版本,其使用了不同的机制,会严重拖慢生成 dex 文件的时间。

退一步讲,考虑到 Kotlin 编译器与 Compose 编译器插件为我们所做的事情,如位置记忆化、细粒度重组等工作,构建时间能够 减少 29%, 可以说十分惊人。您可以查看我们发布的文章来了解更多:

注意事项

关于上面的所有结果,有些事项需要注意:

与新功能相关工作

在这 11 个月中,我没有在 Tivi 上进行过重大的新功能开发,但我也没有刻意限制自己。我进行了许多无关乎迁移的修改,可能会使结果产生偏差。

依赖更新

在这 11 个月的迁移过程中,许多依赖都更新了。其中的大多数均为运行时依赖库,因此最有可能影响 APK 大小这一指标。

我也更新了 Gradle (从 6.0.1 到 7.0.0)、Android Gradle Plugin (3.6.0 到 7.0.0-alpha14) 以及 Kotlin (1.3.61 到 1.4.32),这些更新都可能大幅影响构建速度。

Compose 仍处于 beta 阶段

这点是最为显而易见的。Compose 目前仍处于 beta 阶段,所以所有的结果均来自开发过程中一个适时的快照。当今年晚些时候到达 1.0 版时,可以重新运行这些测试,并对比产生了哪些差异。

总结

如果我们看了结果和注意事项,就应该注意到我们并没有把 🍎 与 🍏 做比较,而更像是在拿 🍎 与比它甜了点的亲戚 🍐 进行比较,因此也不应该做出太多评价。

把水果类比放在一边,我觉得对我来说最大的收获,是 Compose 对于大多数开发者指标产生的影响是积极 (或中性) 的。考虑到这一点,再加上 Compose 大大提高了开发人员的生产力,对我来说,Compose 无疑是 Android UI 开发的未来。

感谢 Nick Butcher、Jose Alcérreca 和 Jolanda Verhoef。

版权声明

禁止一切形式的转载-禁止商用-禁止衍生 申请授权

脉脉不得语
脉脉不得语
Zhengzhou Website
Android Developer | https://androiddevtools.cn and https://androidweekly.io Funder | GDG Zhengzhou Funder & Ex Organizer | http://Toast.show(∞) Podcast Host

你已经成功订阅到 Android 开发技术周报
太棒了!接下来,完成检验以获得全部访问权限 Android 开发技术周报
欢迎回来!你已经成功登录了。
Unable to sign you in. Please try again.
成功!您的帐户已完全激活,您现在可以访问所有内容。
Error! Stripe checkout failed.
Success! Your billing info is updated.
Error! Billing info update failed.
🍗