2. AGP 3.x到4.x的关键变化

从AGP 3.x升级到4.x,说实话,不是那种"改个版本号就完事"的小打小闹。Gradle API和DSL这两块,变动挺大的。我当年带团队升级时,第一次跑构建就炸了十几个报错——嗯,从那以后我学乖了,每次升级前都会先把变化捋清楚。

AGP 3.x → 4.x 关键变化图谱 AGP 3.x → 4.x 关键变化 Gradle API变更 DSL重构 构建脚本影响 废弃旧API方法 引入Provider API 闭包转Block 命名空间独立化 构建逻辑迁移 版本依赖适配 ⚠ 核心变化总结 API层:废弃旧方法 → 引入Provider懒加载 | DSL层:闭包→Block、namespace独立 | 脚本层:配置迁移、版本适配

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 → compileSdk
  • buildToolsVersion → buildTools
  • minSdkVersion → minSdk
  • targetSdkVersion → targetSdk
  • applicationId 保留,新增 namespace
  • minifyEnabled → 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 ->
            // 处理输出
        }
    }
}
⚠ 我曾经踩过的坑:升级到4.x后,发现自定义构建逻辑完全不执行。查了半天,原来是 applicationVariants.all 的回调时机变了——4.x 里变体配置变成了懒加载,必须用 onVariants 才能保证在正确阶段执行。记住:别直接 copy 旧代码,先理解新API的执行时机。

2.4 迁移建议:三步走,稳一点

说了这么多变化,怎么落地?我建议分三步走:

  1. 先扫雷:用 --warning-mode=all 跑一次构建,把所有的废弃API警告列出来。我一般会导出一个报告,逐条过。
  2. 再改脚本:按上面的对照表,把 compileSdkVersion、buildToolsVersion 等全部改成新写法。同时把 namespace 加上。
  3. 最后适配:检查自定义 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层面统一配置方式,脚本层面要求更规范的写法。虽然迁移过程有点折腾,但你想想看——这些改动换来的,是每次构建省下的几十秒甚至几分钟,长期来看是非常值得的。