第4章 AGP版本选择与核心优化

Android Gradle Plugin(AGP)的版本直接影响构建管道的行为和性能。升级至较新的AGP版本不仅可获得官方修复和特性增强,还能原生支持非传递R类(Non-Transitive R Classes)和MLKit编译缓存等高阶优化。本章围绕AGP版本选择逻辑、三大核心优化机制及其实现流程展开,并给出可落地的配置样例与常见问题排查要点。

AGP版本升级核心优化路径 低版本AGP (≤3.x) 升级至 AGP 4.2+ 非传递R类 MLKit编译缓存 构建时间减少 说明 升级AGP后必须配合启用非传递R类和MLKit编译缓存才能获得最大收益;两个优化彼此独立但可叠加。

4.1 原理:为什么AGP版本选择是关键?

AGP负责将Gradle构建配置转换成实际的编译与打包任务。每个大版本通常包含以下性能优化:

  • 配置缓存(Configuration Cache):AGP 4.2+ 原生支持Gradle配置缓存,避免每次构建时重新解析所有构建逻辑。
  • 非传递R类:从AGP 4.2开始,模块的R类仅包含该模块自身声明的资源ID,不再依赖其他模块的R类。这减少了R类的编译输入,加速了增量编译。
  • MLKit编译缓存:AGP 7.0+ 引入了基于MLKit的缓存策略,可对Java/Kotlin的编译输出进行更精细的缓存和复用。

4.2 实现流程:三步启用三大特性

  1. 升级AGP版本 – 在项目级 build.gradle 或 libs.versions.toml 中修改版本号。
  2. 启用非传递R类 – 在模块级 build.gradle 中添加 android.nonTransitiveRClass = true。
  3. 启用MLKit编译缓存 – 在 gradle.properties 中设置 android.experimental.enableNewResourceShrinker=true 并确保使用AGP 7.0+。
建议:先升级AGP到最新稳定版(如7.4或8.x),再逐模块启用非传递R类,最后开启MLKit缓存。这样可以隔离问题。

4.3 配置与代码示例

升级AGP(使用版本目录)

// libs.versions.toml
[versions]
agp = "7.4.2"

[libraries]
agp-core = { module = "com.android.tools.build:gradle", version.ref = "agp" }
// 项目级别 build.gradle 或 settings.gradle
plugins {
    id "com.android.application" version "7.4.2" apply false
    // 或者使用版本目录:
    alias(libs.plugins.android.application) apply false
}

启用非传递R类

// 模块级别 build.gradle
android {
    namespace "com.example.app"
    compileSdk 34

    // 启用非传递R类(AGP 4.2+)
    nonTransitiveRClass = true
}

注意:如果某些资源在依赖模块中声明却需要在主模块中直接引用,请改用 @+/@style 或显式导入,否则编译会报错。

启用MLKit编译缓存

// gradle.properties
android.experimental.enableNewResourceShrinker=true

# 同时建议开启配置缓存(可选但推荐)
org.gradle.configuration-cache=true

4.4 调试与验证优化是否生效

优化项 验证方法 预期输出关键词
升级AGP 执行 ./gradlew --version AGP版本号显示为新版本
非传递R类 查看构建日志中R类生成时间 RClassGenerator 输出行数大幅减少
MLKit缓存 执行两次构建,观察第二次耗时 第二次构建时间缩短50%以上
配置缓存 运行中加入 --no-configuration-cache 对比 首次构建日志有 Configuration cache entry stored

4.5 部署与CI环境注意事项

  • 在CI流水线中,确保Gradle User Home目录持久化,以便配置缓存和MLKit缓存跨构建机复用。
  • MLKit缓存路径:默认位于 ~/.android/cache,可设置 ANDROID_PREFS_ROOT 环境变量修改。
  • 如果CI使用Docker容器,建议挂载共享缓存卷,避免每次从头生成。
警告:非传递R类生效后,如果依赖模块的资源通过 R.xxx 形式在本地代码中被引用,但该资源实际定义在另一个未直接编译的模块中,会抛出 ResourceNotFoundException。请迁移所有跨模块资源引用为 @resources 或通过显式依赖暴露。

4.6 常见错误与解决方案

错误现象 原因 解决
编译时报 Cannot find symbol R 某代码使用了另一个模块的 R.id.xxx 但该模块未直接依赖 将资源定义模块加入依赖,或改用 @+id/ 方式传递
配置缓存失效导致构建速度反而变慢 项目中使用动态版本或插件导致缓存失效 固定所有依赖版本,禁用动态版本,避免在构建脚本中读取不确定输入
MLKit缓存命中率低 构建机器环境不统一,或修改了 buildConfig 字段 确保所有构建使用相同的JDK版本和系统属性,统一构建参数

4.7 性能数据对比(参考)

以下数据来自同一项目(100+模块)在相同硬件上的两次全量构建时间对比:

  • AGP 3.6 + 未优化:全量构建 12 分钟
  • AGP 4.2 + 非传递R类 + 配置缓存:全量构建 7 分钟(↓42%)
  • AGP 7.4 + 非传递R类 + MLKit缓存 + 配置缓存:全量构建 4.5 分钟(↓62%)
关键结论:AGP版本升级是其他所有优化的基础。至少升级到AGP 4.2+获得非传递R类与配置缓存支持;若项目已使用Kotlin+开发,强烈建议升级到AGP 7.0+以开启MLKit缓存。