AGP 升级概述与影响归纳

说实话,每次 AGP 升级,团队里总有人心里打鼓。我做了这么多年 Android 构建,自己也被 AGP 折腾过不少回。这一章咱们先捋清楚:AGP 升级到底改了啥?对 CI 分层又意味着什么?

AGP 版本演进:从增量到质变

咱们先看一张表,把近几个大版本的关键变化列出来。我自己常用的对比方式是 —— 盯住构建系统 API、变体系统、DSL 结构这三条线。

AGP 版本 核心变化点 对 CI 的直接影响
7.x → 8.0 变体 API 改为 Variant API,移除 VariantFilter 自定义变体逻辑需重写,CI 脚本中基于 filter 的配置失效
8.0 → 8.3 引入 BuildConfig API、统一 namespace 声明 gradle.properties 中配置项迁移,CI 需要手动对齐
8.3 → 8.5 非传递 R 类、配置缓存完全开启 CI 缓存策略调整,避免旧缓存导致 R 类丢失
8.5+ Kotlin DSL 优先、移除 deprecated API Groovy 脚本需要逐步迁移,CI 中 gradle wrapper 版本强制更新

你看,每次升级都不是小打小闹。我遇到过最惨的一次 —— 团队从 AGP 7.4 直接跳到 8.2,结果 CI 上所有自定义变体任务全部报错,排查了两天才发现是 Variant API 换了签名。

升级后 CI 面临的「三层震荡」

AGP 升级对 CI 的影响,我认为可以归纳为三个层面。嗯,我习惯叫它「三层震荡」。

  1. 构建脚本层:DSL 变更、插件 API 废弃、配置选项迁移。这部分最直接,改 build.gradle 就完了。
  2. 任务图与执行层:新 AGP 可能会新增或合并 task,比如 transformClassesWith 变为 dexBuilder,CI 依赖的特定 task 路径全断。
  3. 缓存与产物层:BuildCache 结构变了,Gradle 配置缓存格式升级,旧缓存不能直接复用。

说白了,就是你的 CI 流水线从「参数配置」到「任务编排」到「产出物管理」全都要过一遍筛子。我曾经在一个大型项目里为了适配 AGP 8.0 的配置缓存,把整个 CI 的 gradle daemon 参数调了三天。嗯,这件事之后我就养成了「先在小分支试升级,再铺到全团队」的习惯。

核心变化点深度解析

1. 变体系统重构

AGP 8.0 把原来的 VariantFilter 和 VariantOutput 全废了,新 API 用 VariantSelector 配合 onVariants 回调。你想想看,以前在 CI 里通过 variantFilter {... } 排除 debug 变体,现在得改成 androidComponents { onVariants { ... } }。写法变了,CI 脚本中的 groovy 或 kotlin 代码都得跟着改。

// 旧方式(AGP 7.x)
android {
    variantFilter { variant ->
        if (variant.buildType.name == 'debug') ignore = true
    }
}

// 新方式(AGP 8.x)
android {
    androidComponents {
        beforeVariants { variant ->
            if (variant.buildType == 'debug') variant.enabled = false
        }
    }
}

我曾经踩过的坑:升级后直接复制了旧脚本,CI 上跑了两天没发现变体没过滤掉,导致构建产物翻倍。建议升级后先跑一次 ./gradlew tasks 检查所有变体是否正确生成。

2. 配置缓存与构建缓存

AGP 8.3+ 开始强制要求 Gradle 配置缓存与构建缓存配合使用。但问题来了:旧版 CI 中可能用了 cache.caching 自定义缓存路径,升级后路径格式变了,缓存命中率直接掉到 0%。

我个人的做法是升级后先在本地全量构建一次,生成新缓存,然后把 CI 机器的 ~/.gradle/caches 清掉,重新预热。别偷懒,否则你会在 PR 构建上浪费大量时间。

小技巧:在 CI 的 gradle.properties 中设置 org.gradle.caching=true 和 android.buildcache.enabled=true,并指定统一缓存目录,避免跨版本冲突。

3. BuildConfig 与 namespace 迁移

AGP 8.0 要求每个模块必须显式声明 namespace,不再从 applicationId 或 AndroidManifest.xml 推断。这对 CI 中的多模块构建影响很大 —— 以前你可能在 CI 脚本里通过 project.android.namespace 动态生成资源 ID,现在必须硬编码或者用 android.namespace DSL 固定。

// CI 脚本中需要确认 namespace 是否与 tasks 匹配
tasks.whenTaskAdded { task ->
    if (task.name.contains("generate") && task.name.contains("Debug")) {
        // 检查 namespace 是否符合预期
        def ns = project.android.namespace
        if (ns != "com.example.app") {
            throw new GradleException("Namespace mismatch: $ns")
        }
    }
}

一张图看懂 AGP 升级后 CI 分层调整

下面这张图是我基于多个项目经验总结出来的。它把 CI 拆成四个层次,每个层次都有 AGP 升级带来的「冲击点」。你可以把它贴在 CI 文档首页,排查问题时就对着图过一遍。

AGP 升级后 CI 分层调整模型 层1:基础设施层 JDK 版本升级 · Gradle 版本强制更新 · Gradle Daemon 配置 AGP 最低版本要求 → JDK 17+ (AGP 8.x) 层2:构建脚本层 Variant API 重构 · DSL 变化 · namespace 强制声明 · BuildConfig 迁移 CI 脚本中 variantFilter/onVariants 改写 层3:任务图与执行层 Task 名称变更 · Task 依赖关系变化 · 配置缓存适配 依赖的 task 路径(如 transformClasses)可能消失 层4:缓存与产物层 BuildCache 格式升级 · 配置缓存不兼容 · 产物目录结构调整 旧缓存无效,需清空重新预热 冲击点:脚本改、任务断、缓存废 → 需要分层逐一排查

我的升级检查清单

每次 AGP 大版本升级前,我都会在 CI 上跑一遍这个清单。建议你也存一份。

  • ✅ 确认 JDK 版本满足 AGP 最低要求(8.x 需要 JDK 17+)
  • ✅ 更新 Gradle wrapper 到最新补丁版本(如 8.7+)
  • ✅ 在独立分支上跑一次全量构建,检查所有 task 名称是否变更
  • ✅ 对照官方迁移指南,逐个修改 build.gradle 中的 deprecated API
  • ✅ 清空本地与 CI 的缓存目录,重新预热
  • ✅ 检查所有自定义 Plugin 和 Task 是否适配新 Variant API
  • ✅ 执行 CI 流水线,观察构建时间变化和缓存命中率
  • ✅ 回滚预案:保留上一个 AGP 版本的构建脚本备份,至少一个迭代周期

核心原则:别一次性升级所有项目。我自己的做法是「先升核心模块,再铺到全量」。CI 分层调整的本质就是让每个层能独立验证,互不拖累。