2. AGP 3.x到4.x的关键变化
从AGP 3.x升级到4.x,说实话,不是那种"改个版本号就完事"的小打小闹。Gradle API和DSL这两块,变动挺大的。我当年带团队升级时,第一次跑构建就炸了十几个报错——嗯,从那以后我学乖了,每次升级前都会先把变化捋清楚。
2.1 Gradle API变更:从"直接调用"到"懒加载"
AGP 4.x最大的底层变化,就是把一大批直接调用的API换成了基于 Provider 和 Property 的懒加载模式。说白了,就是让构建系统"按需干活",别一股脑全执行。
我在项目中遇到过好几次,3.x时的自定义Task,升级到4.x直接编译报错。最典型的就是 TaskContainer 的用法变了:
// AGP 3.x 写法(直接创建)
task myTask(type: Copy) {
from 'src'
into 'dest'
}
// AGP 4.x 推荐写法(懒注册)
tasks.register("myTask", Copy::class.java) {
from("src")
into("dest")
}
为什么会这样?因为 register() 返回的是 TaskProvider,它在配置阶段不会立即实例化 Task,只有等到真正执行时才创建。你想想看,如果一个项目有几百个 Task,这种懒加载带来的增量构建提速是非常明显的。
另一个我踩过的坑是 Configuration API。3.x 里用 compile、provided、apk 这些配置,到4.x全部废弃,必须迁移到 implementation、api、compileOnly:
// AGP 3.x(已废弃)
dependencies {
compile 'com.example:lib:1.0'
provided 'com.example:annotations:1.0'
}
// AGP 4.x(强制使用)
dependencies {
implementation 'com.example:lib:1.0'
compileOnly 'com.example:annotations:1.0'
}
./gradlew assemble --warning-mode=all 跑一遍,把所有废弃API的警告都揪出来。我在团队里定了个规矩——任何 warning 都不准带到生产环境。
2.2 DSL重构:闭包变Block,命名空间独立
DSL这块的变化,用起来感受最直接。以前写 compileSdkVersion 29,现在写成 compileSdk 29。少写几个单词,但背后是Google对整个配置体系的重新设计。
来,看个对比就明白了:
// AGP 3.x DSL
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.example.app"
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
// AGP 4.x DSL
android {
compileSdk 31
buildTools "31.0.0"
namespace "com.example.app" // ← 新增!独立命名空间
defaultConfig {
applicationId "com.example.app"
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"
}
buildTypes {
release {
isMinifyEnabled = true // ← 布尔属性加 is 前缀
proguardFiles(getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro')
}
}
}
注意 namespace 这个属性。在3.x里,包名被 applicationId 和 AndroidManifest.xml 里的 package 属性混着用,经常搞混。4.x把 namespace 独立出来,专门负责代码的 R 文件生成和资源访问,逻辑清晰多了。
还有一个细节:buildTypes 内部的配置,从 Groovy 的闭包风格转向了更严格的 Kotlin DSL 风格。比如 minifyEnabled true 变成了 isMinifyEnabled = true。嗯,这里要注意,如果你用 Kotlin DSL 写构建脚本,这个变化是强制性的。
关键变化清单:
compileSdkVersion→compileSdkbuildToolsVersion→buildToolsminSdkVersion→minSdktargetSdkVersion→targetSdkapplicationId保留,新增namespaceminifyEnabled→isMinifyEnabled(Kotlin DSL 下)
2.3 对构建脚本的影响:版本匹配是头等大事
API和DSL都变了,构建脚本自然得跟着改。但最让我头疼的,其实是AGP版本和Gradle版本的匹配关系。版本没对上,构建直接挂掉是常有的事。
| AGP 版本 | 最低 Gradle 版本 | 推荐 Gradle 版本 | 主要变化 |
|---|---|---|---|
| 3.6.x | 5.6.4 | 6.1.1 | 最后的3.x稳定版 |
| 4.0.x | 6.1.1 | 6.5 | Provider API 引入,namespace 独立 |
| 4.1.x | 6.5 | 6.8 | DSL 简化,构建缓存增强 |
| 4.2.x | 6.7.1 | 7.0 | 废弃API清理,Kotlin DSL 首选 |
我个人习惯在升级AGP之前,先去 Gradle 官方兼容性表 确认版本对应关系。别小看这一步,我见过太多人因为版本不匹配,卡在构建初始化阶段半天找不到原因。
除了版本匹配,脚本本身也需要调整。举个例子,3.x 里自定义 android.applicationVariants.all 遍历变体,到4.x 推荐用 onVariants 或 variantFilter:
// AGP 3.x
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
// 处理输出
}
}
// AGP 4.x
android {
onVariants { variant ->
variant.outputs.forEach { output ->
// 处理输出
}
}
}
applicationVariants.all 的回调时机变了——4.x 里变体配置变成了懒加载,必须用 onVariants 才能保证在正确阶段执行。记住:别直接 copy 旧代码,先理解新API的执行时机。
2.4 迁移建议:三步走,稳一点
说了这么多变化,怎么落地?我建议分三步走:
- 先扫雷:用
--warning-mode=all跑一次构建,把所有的废弃API警告列出来。我一般会导出一个报告,逐条过。 - 再改脚本:按上面的对照表,把
compileSdkVersion、buildToolsVersion等全部改成新写法。同时把namespace加上。 - 最后适配:检查自定义 Task 和 Plugin,把
TaskContainer.create()换成register(),把变体遍历换成onVariants。
小技巧:AGP 4.x 引入了 android.buildFeatures 块,可以按需开启/关闭构建功能(比如 DataBinding、ViewBinding)。我个人强烈建议在迁移时顺手把这些配置整理好,后面维护会省很多事。
// 按需开启构建特性
android {
buildFeatures {
viewBinding = true
dataBinding = false
compose = true
}
}
总结一下:AGP 3.x到4.x的变化,本质上是Google为了让构建系统更快、更稳、更可维护,做的一次"大扫除"。API层面引入懒加载,DSL层面统一配置方式,脚本层面要求更规范的写法。虽然迁移过程有点折腾,但你想想看——这些改动换来的,是每次构建省下的几十秒甚至几分钟,长期来看是非常值得的。