案例研究

Grab 借助 R8 和配置文件引导的优化,将 ANR 发生率降低了 32%,并将启动时间缩短了 35%

阅读用时:5 分钟

Grab 是东南亚领先的超级应用,通过一个应用为八个国家/地区的数百万用户提供配送、出行和金融服务。Grab 的代码库已有 10 年历史,包含超过 1,000 万行代码和数百个第三方库,采用 Bazel 和 Gradle 混合设置构建。随着时间的推移,Grab 团队面临着三大日益严峻的挑战:应用无响应 (ANR) 发生率居高不下;随着超级应用不断添加新服务,应用大小持续增长;启动时间逐年缓慢增加。通过启用 R8 优化、优化保留规则以及实现基准配置文件和启动配置文件,Grab 将用户感知的 ANR 发生率降低了 32%,将应用下载大小缩减了约 30%,并将启动时间缩短了 35%。

消除发布后 ANR 激增问题,打造更流畅的用户体验

在优化构建流程之前,Grab 曾面临冷启动延迟和应用无响应 (ANR) 峰值问题,这些问题在发布新更新后尤为明显,主要是因为应用初始化期间读取大量 DEX 文件产生了开销。

虽然 Google Play 的云配置文件为整个用户群的标准流程提供了优化,但它们并未优化 Grab 的关键用户历程所需的所有初始化路径,例如在应用启动期间初始化 Dagger 依赖项图。对于初始分发中未涵盖的代码路径,设备在后续几天内依赖于 JIT 编译和本地后台优化。通过实现自定义 基准配置文件,他们将这些关键用户历程直接提供给 Google Play 的云配置文件,从而扩大了优化覆盖范围,使这些关键路径在安装时立即编译为机器代码,从而解决了这一问题

ANDDM_Grab_Inline_Quote_ALT.png

Grab 乘客应用核心团队的首席软件工程师 Nguyen Van Minh 解释道:“我们发现,每次发布每周应用版本时,ANR 发生率都会飙升。”

为了消除 ANR 并缩短启动时间,Grab 实施了 R8 优化、基准配置文件和启动配置文件。配置文件引导的优化与 R8 优化器相结合,使冷启动速度立即提升了 12%(得益于 R8),并额外提升了 15%(得益于基准配置文件),从而将启动时间缩短了 27%,并有效消除了发布后的 ANR 峰值。

Grab 分三个战略阶段执行了这项应用优化:在兼容模式下启用 R8 优化、过渡到 R8 完整模式,然后使用配置分析器优化 Keep 规则并添加 CI/CD 治理。

第 1 阶段:启用 R8 优化(2025 年发布)

在第一阶段,Grab 弃用了旧版默认 Proguard 文件,改用 proguard-android-optimize.txt,从而启用了 R8 优化。由于代码库已有十多年历史,且包含超过 1, 000 万行代码,因此启用优化器需要解决旧版配置和反射使用问题。

为了最大限度地发挥此阶段的影响,Grab 发布了启用 R8 兼容模式的应用更新,其中包含基准配置文件和启动配置文件。这些举措立竿见影,让用户感知的 ANR 发生率降低了 25%,应用下载大小缩减了 16%,冷启动时间整体缩短了 27%。

第 2 阶段:R8 完整模式和迁移(2026 年发布)

此阶段在首次发布九个月后启动,将应用切换到了 R8 完整模式,从而实现了进一步优化。虽然 Retrofit、Firebase 和 Jetpack Compose 等标准库使用其内置规则与 R8 无缝协作,但迁移其余庞大代码库的过程需要解决特定的高度依赖反射的代码路径:

为了迁移到 R8 完整模式,该团队采用了最新 Gson 版本中的消费者保留规则,以确保无缝的 JSON 解析。他们还解决了 Java 动态代理的优化难题,这些代理使用反射在运行时生成接口实现。由于 R8 无法在编译期间检测到这种运行时使用情况,因此它会剥离或重命名必要的类,从而导致应用崩溃。该团队没有应用会增加应用大小的宽泛例外规则,而是隔离了依赖反射的特定类,并实施了高度针对性的保留规则,从而同时保证了应用稳定性和优化效率

最终,R8 完整模式带来了巨大的复合改进,包括应用下载大小额外缩减 14%、启动时间额外缩短 10%,以及用户感知的 ANR 发生率额外降低 10%。迁移到 R8 全模式后,Grab 的代码库获得了以下 R8 优化分数。

  • 优化得分:72%
  • 缩减得分:73%
  • 混淆得分:79.6%

这些分数提供了有关 R8 配置的反馈,并指出了是否需要优化 Keep 规则以最大限度地发挥 R8 的优化效果。

第 3 阶段:优化保留规则 

在优化历程的最后阶段,Grab 专注于建立长期的代码库健康状况、持续的性能监控和严格的编译治理。为了系统地衡量规则的健康状况,并防止在持续的发布周期中出现配置回归,该团队正在将 R8 配置分析器集成到其评估工作流中。借助此工具,Grab 可以深入了解其保留规则在多大程度上允许 R8 优化、缩减和混淆应用。

通过在 R8 完整模式的基础上,有条不紊地精简旧版保留规则,Grab 在分析器评估的所有三个核心维度上都实现了显著改进:

  • 优化得分:从 72% 提高到 78.2%
  • 缩减得分:从 73% 提高到 79.1%
  • 混淆得分:从 79.6% 提高到 81.7%

除了提高这些诊断分数之外,这个专门的 keep 规则优化阶段还使应用下载大小进一步缩减了约 3.6%。更重要的是,通过跟踪这些指标,工程团队获得了所需客观数据,从而能够实施自动化合并前 CI 护栏,确保新引入的代码遵循严格的 keep 规则标准,并长期保持来之不易的性能提升。

成效

R8 优化器推出后,性能得到了以下关键提升:

优化阶段ANR大小冷启动
第 1 阶段:R8 优化器(2025 年发布)~25% 降幅16% 降幅~27% 的提升
第 2 阶段:R8 完整模式(2026 年发布)10% 降幅14% 降幅10% 的提升
保留规则优化-3.6% 降幅-
总计(累计)减少约 32.5%减少约 30.4%提升约 35%

技术挑战 

在 Grab 拥有十年历史的代码库中启用 R8 优化器,带来了大规模的关键技术障碍:

  • 测试:经过十年的积累和反思,我们发现,在全面优化的情况下,会出现一些极端情况。由于无法对数百万行代码进行详尽测试,团队采取了增量式、高优先级的测试方法,专门针对被缩减的保留规则所涵盖的代码覆盖率进行测试,并迭代优化这些规则,使其仅针对反射所需的特定类、方法或字段,从而最大限度地发挥 R8 优化的效果
  • 构建延迟和混合架构:Grab 的混合构建系统(Bazel 用于调试,Gradle 用于 R8 发布 build)意味着实验性 R8 更改在远程 CI 中最多需要 2 小时。为了弥合反馈差距,Grab 调整了其每日 QA build,使其与生产 R8 行为保持一致,并自动收集关键 CI build 工件(例如 mapping.txt、configuration.txt),以简化调查。
  • 调试周期缓慢:在数百个经过优化的类中追踪根本原因,给精益团队带来了沉重负担。Grab 通过 Gradle 自动将映射文件上传到 Firebase Crashlytics,并为 Play 管理中心捆绑去混淆映射,从而解决了这一问题,实现了对运行时问题的即时、准确符号解析

R8 对代码的长期影响

除了立竿见影的性能提升,启用 R8 优化器还为 Grab 的庞大代码库带来了永久性的结构优势。Grab 团队使用开源工具 app-sizer 衡量了 R8 的影响,并观察到 R8 永久降低了应用中每行代码在下载大小方面的成本。

ANDDM_Grab_Inline_Metric.png

在 AI 辅助开发时代,代码量迅速增加,这种优化变得更加重要。如果不使用 R8,代码扩展会按比例增加下载大小。R8 可确保在代码扩展率相同的情况下,内存占用量大幅减少。

注意:方法:app-sizer 的 Kotlin/Java 对应用下载大小(代表性设备)的贡献,除以生产 Kotlin LOC(cloc;仅限主要源代码集;不包括测试、模拟和示例模块)。

Keep 规则管理和安全措施

为了防止规则退化并长期保持 R8 优化效果,Grab 计划将自动化合并前防护机制直接集成到其 CI 流水线中。根据此政策,新保留规则受到严格限制,任何额外规则都需要明确的理由和审核。Grab 团队在 CI 中跟踪 R8 优化得分。在此基础上,该团队计划将这些指标用作持续反馈环,以便在合并之前捕获回归,并在每个发布周期中保持优化成果。

R8-rules-score (1).png

图 1:CI 信息中心,用于跟踪 Grab build 中 R8 缩减、优化和混淆得分随时间的变化,以便主动发现和防止各版本出现回归。

总结 

面对庞大且不断成熟的代码库带来的挑战,Grab 的 Pax App Core 团队积极应对启动时间增加和发布后性能峰值问题,他们实施了基准配置文件,并持续改进 R8 配置。该团队将用户感知的 ANR 减少了约 32.5%,将应用下载大小缩减了约 30.3%,并将冷启动时间缩短了约 35%,同时从根本上提高了代码库的扩缩效率。

这些优化为东南亚数百万用户提供了更快、更稳定的超级应用体验,同时为 Grab 的工程师提供了更快速构建功能的工具,而不会导致下载大小快速膨胀。

如需开始使用 R8 并了解详情,请访问 - d.android.com/r8。

继续阅读