AGP 9.0升级攻略:挥别技术旧疾,迎接开发新程
升级前的准备
Android Gradle Plugin(AGP)9.0 的发布,在 Android 开发领域掀起了一阵波澜。它带来了一系列重大的变革,如内置 Kotlin 支持、全新的 DSL 接口以及对 KMP 项目结构的调整 ,这些改变有望提升开发效率和项目的整体质量。不过,在享受这些新特性之前,我们需要做好充分的准备工作,以确保升级过程的顺利进行。
在升级之前,第一件事就是备份你的项目代码。这一步至关重要,它就像是给你的项目上了一份保险。你可以使用版本控制系统,如 Git,进行一次完整的提交,并创建一个新的分支。这样,一旦升级过程中出现问题,你可以迅速回滚到之前稳定的版本,避免造成不必要的损失。比如,在一个多人协作的项目中,小李在升级 AGP 之前没有备份代码,结果升级后项目出现了大量编译错误,由于没有备份,他不得不花费大量时间去修复这些错误,严重影响了项目进度。
接下来,就是全面了解 AGP 9.0 的变化。你可以查阅官方文档,深入了解新特性、废弃的 API 以及行为变更。例如,AGP 9.0 默认启用了内置 Kotlin 支持,这意味着你不再需要单独应用 Kotlin Android 插件。同时,旧的 DSL 接口被移除,需要迁移到新的 public DSL interfaces;KMP plugin 不再能和 com.android.application/com.android.library 同模块共存,需要对项目结构进行相应调整。了解这些变化,能让你在升级过程中有的放矢,提前做好应对措施。
AGP 9.0 的核心变化
新 DSL 接口
AGP 9.0 开始只暴露新的 public DSL interfaces,旧的 DSL 类型,比如历史上很多插件或脚本会强转到 BaseExtension 之类,将不再提供 。同时,旧的 variant API,如 applicationVariants 等,也一并被切断,需要迁移到 androidComponents API。这一变化就像是给项目的构建逻辑换了一套 “语言”,虽然新 “语言” 更高效、更现代,但我们需要重新学习和适应。
从 Google 的角度来看,这是一次清理历史包袱的重要举措,终于可以在主版本把 “历史兼容层” 拔掉了 。但对于开发者而言,这意味着自己写的 Gradle 插件或 buildSrc 里强转旧类型的代码可能会直接崩溃,依赖 applicationVariants、libraryVariants 的逻辑也需要改成 androidComponents。比如,在一个项目中,原本通过 applicationVariants 来配置不同构建变体的代码,在升级到 AGP 9.0 后就无法正常工作了,需要按照新的 androidComponents API 进行改写。
不过,官方也提供了 android.newDsl=false 的回退选项,但要注意的是,AGP 10 会移除这个退路,所以虽然可以暂时过渡,但适配新的 DSL 接口是必然的趋势。
内置 Kotlin
AGP 9.0 默认启用了内置 Kotlin 支持,这意味着你不再需要给 Android module 单独应用 org.jetbrains.kotlin.android 插件就能编译 Kotlin,也不再需要声明 KGP 版本。这一改变的初衷是为了简化项目配置,让开发更加便捷。然而,它也带来了一些副作用。
由于项目 / 插件生态里 “默认的 KGP/Kotlin Android 插件” 的假设被打破,可能会引发引用和依赖冲突。例如,你可能会遇到这样的错误提示:“Failed to apply plugin 'org.jetbrains.kotlin.android'. -> Cannot add extension with name 'kotlin', as there is an extension already registered with that name.” 或者 “Failed to apply plugin 'com.jetbrains.kotlin.android' -> The 'org.jetbrains.kotlin.android' plugin is no longer required for Kotlin support since AGP 9.0”。
为了解决这些问题,我们需要进行一系列的迁移操作。首先,移除 kotlin - android 插件,即移除 org.jetbrains.kotlin.android(或 kotlin - android),并删除所有相关的 apply 语句。其次,迁移 kotlin - kapt 插件,建议迁移到 KSP。如果暂时无法迁移到 KSP,可以将 kotlin - kapt 插件替换为 com.android.legacy - kapt 来尝试适配。然后,迁移 android.kotlinOptions {} DSL,将其迁移到 kotlin.compilerOptions {} 。最后,迁移 kotlin.sourceSets {} DSL,另外可以使用 variant API 的 addStaticSourceDirectory 或 addGeneratedSourceDirectory 方法。
此外,AG 9.0 还引入了对特定 Kotlin Gradle Plugin 版本的运行时依赖。如果使用的 KGP 版本低于 2.2.10,Gradle 会自动将其升级到 2.2.10;同样,如果使用的 KSP 版本低于 2.2.10 - 2.0.2,AGP 也会将其升级到 2.2.10 - 2.0.2 以匹配 KGP 版本。当然,你也可以选择通过 enableKotlin 关闭这个默认支持,然后使用较低版本的 KGP 或 KSP ,具体配置如下:
android {
enableKotlin = false
}
buildscript {
dependencies {
// For KGP
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") {
version { strictly("KGP_VERSION") }
}
// For KSP
classpath("com.google.devtoolsksp:symbol-processing-gradle-plugin") {
version { strictly("KSP_VERSION") }
}
}
}
但官方也明确表示,最低支持 2.0 版本,并且降级带来的不兼容问题需要自行负责。另外,也可以通过在 gradle.properties 文件中设置 android.builtInKotlin=false 来禁用内置 Kotlin。
KMP 插件的变化
对于 KMP 项目,使用 AGP 9.0 + 时,KMP Gradle plugin 不再能和 com.android.application/com.android.library 在同模块共存,这就要求我们对项目结构进行调整。比如,原本在一个模块中同时包含 KMP 和 Android Application 的配置,在升级后就无法正常工作了。
解决方案是把 Android 入口,如 Activity/Application 等,抽到单独的 Android app module,共享代码 module 改成使用新的 Android - KMP library plugin:com.android.kotlin.multiplatform.library。具体来说,就是将原来的 composeApp/src/androidMain 整个挪到 androidApp/src/ ,因为 AGP 9 要求 Android 应用入口,比如 MainActivity.kt,不再待在 KMP + Android Application 同模块中,入口必须属于 androidApp 这种 application module 才能正常打包与运行。不过,有 expect/actual 声明必须继续留在共享模块 composeApp 的 source sets 里,以保证其它平台也能使用。
如果你的 actual 实现严重依赖 Activity 级别的 Context 或生命周期,可能需要重构为依赖注入或回调接口,否则在 Library 级别的 Shared Module 中很难处理。同时,把 androidApp/src/androidMain 重命名为 main ,因为 androidApp 是一个普通 Android application module,它不走 KMP 的 source set 命名体系。还要清理 composeApp 里原本的 androidMain.dependencies ,在 composeApp/build.gradle.kts 中删除 kotlin.sourceSets.androidMain.dependencies {} 这块内容,也就是基于 build variants 的依赖,如 debug/release,需要搬迁或重构,同时按 Android - KMP library plugin 的规则重新组织 androidMain 相关依赖。
目前还能暂时用 android.enableLegacyVariantApi=true 来维持原有结构,但这个 “legacy” 也会在 AGP 10 被彻底移除,同时记得要设置 android.newDsl=false 。
升级实战步骤
检查当前项目配置
在升级之前,首先要查看当前项目中使用的 AGP 版本、Gradle 版本、Kotlin 版本以及相关依赖。这就像是在出发前检查车辆的各项参数,确保你对当前的 “车况” 了如指掌。你可以通过项目级的 build.gradle 文件查看 AGP 版本,在 gradle-wrapper.properties 文件中查看 Gradle 版本,在项目的依赖配置中查看 Kotlin 版本及其他依赖。例如,在 build.gradle 文件中,你可能会看到这样的配置:
buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:8.0.2'
}
}
这表明当前项目使用的 AGP 版本是 8.0.2。通过这种方式,你可以清晰地了解项目的当前配置,为后续的升级做好准备。
更新 Gradle 和 AGP 版本
接下来,就是修改项目级 build.gradle 文件中的 AGP 版本为 9.0.0 ,以及更新 Gradle Wrapper 到兼容 AGP 9.0.0 的版本。这一步就像是给车辆换上新的发动机和轮胎,让它能够适应新的路况。在 build.gradle 文件中,将 AGP 版本修改为:
buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:9.0.0'
}
}
然后,在 gradle-wrapper.properties 文件中,将 distributionUrl 更新为兼容的 Gradle 版本,比如:
distributionUrl=https://services.gradle.org/distributions/gradle-8.5-bin.zip
更新完成后,点击 Android Studio 中的 “Sync Project with Gradle Files” 按钮,同步项目配置。
解决依赖冲突
在升级过程中,可能会遇到各种依赖冲突,如 Kotlin 插件和 KSP 版本冲突。这就像是车辆在行驶过程中遇到了道路障碍,需要及时清理才能继续前行。例如,当你升级 AGP 到 9.0 后,可能会遇到 Kotlin 插件和 KSP 版本不匹配的问题,导致编译失败。此时,你可以通过查看错误日志,找到冲突的依赖项,然后根据官方文档或社区经验,调整依赖的版本或使用 exclude 来排除冲突的依赖。比如,在 dependencies 中添加如下配置来排除冲突的依赖:
implementation('com.example:library:1.0') {
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib'
}
或者,使用 resolutionStrategy 来强制指定依赖的版本:
configurations.all {
resolutionStrategy {
force 'org.jetbrains.kotlin:kotlin-stdlib:1.9.20'
}
}
代码和配置迁移
最后,根据 AGP 9.0 的变化,进行代码和配置的迁移。这一步就像是按照新的地图导航,调整车辆的行驶路线。例如,迁移到新的 DSL 接口,将 kotlinOptions 和 sourceSets 配置迁移到新的位置,以及调整 KMP 项目结构等。
对于新 DSL 接口的迁移,需要将原来使用旧 DSL 接口的代码替换为新的 androidComponents API。比如,原来通过 applicationVariants 来配置构建变体的代码:
android.applicationVariants.all { variant ->
variant.outputs.all {
// 配置输出
}
}
需要修改为:
androidComponents {
onVariants(selector()) { variant ->
variant.outputs.all {
// 配置输出
}
}
}
对于 kotlinOptions 和 sourceSets 配置的迁移,将 kotlinOptions 从 android.kotlinOptions 迁移到 kotlin.compilerOptions ,将 sourceSets 相关配置按照新的规则进行调整。例如,原来的 kotlinOptions 配置:
android {
kotlinOptions {
jvmTarget = "1.8"
}
}
需要修改为:
kotlin {
compilerOptions {
jvmTarget.set("1.8")
}
}
对于 KMP 项目结构的调整,按照前面提到的方法,将 Android 入口抽到单独的 Android app module,共享代码 module 改成使用新的 Android - KMP library plugin:com.android.kotlin.multiplatform.library 。
常见问题及解决方案
在升级 AGP 到 9.0 的过程中,难免会遇到一些问题。这里我们来总结一些常见问题,并提供相应的解决方案,希望能帮助大家顺利升级。
编译错误
在升级 AGP 到 9.0 后,你可能会遇到各种编译错误。例如,代码中使用了旧的 DSL 接口,导致编译失败。此时,你需要仔细查看错误日志,找到错误的具体位置和原因。如果是因为旧的 DSL 接口问题,按照前面提到的方法,将其迁移到新的 androidComponents API。另外,依赖冲突也可能导致编译错误,如 Kotlin 插件和 KSP 版本冲突,这时候就需要根据依赖冲突的解决方案进行调整。
插件不兼容
部分第三方插件可能不兼容 AGP 9.0,导致构建失败。遇到这种情况,首先要检查插件的官方文档,看是否有针对 AGP 9.0 的更新说明。如果插件有更新,及时将其更新到最新版本。如果插件没有更新,你可以尝试联系插件作者,询问是否有计划支持 AGP 9.0,或者在社区中寻求帮助,看是否有其他开发者遇到过类似问题并找到了解决办法。如果实在无法解决,可能需要考虑寻找替代插件。
KMP 项目结构调整问题
在调整 KMP 项目结构时,可能会遇到各种问题,如资源文件的迁移、依赖配置的调整等。例如,在将 Android 入口抽到单独的 Android app module 时,可能会出现资源文件找不到的情况。这时候,需要仔细检查资源文件的路径是否正确,是否按照新的项目结构进行了相应的调整。对于依赖配置,要确保按照新的 Android - KMP library plugin 的规则进行重新组织,避免出现依赖缺失或冲突的问题。如果遇到 actual 实现严重依赖 Activity 级别的 Context 或生命周期的情况,要及时进行重构,以适应新的项目结构。
升级后的优化与验证
优化构建性能
升级到 AGP 9.0 后,为了充分发挥其优势,我们可以采取一些措施来优化构建性能。首先,可以开启并行构建,让 Gradle 在多个 CPU 核心上并行执行任务,从而加快构建速度。在 gradle.properties 文件中添加org.gradle.parallel=true即可开启并行构建。例如,在一个包含多个模块的项目中,开启并行构建后,构建时间从原来的 5 分钟缩短到了 3 分钟。
其次,配置构建缓存也是提升性能的关键。构建缓存可以复用之前的构建结果,避免重复执行相同的任务。在 gradle.properties 文件中添加org.gradle.caching=true来启用构建缓存 。同时,还可以配置缓存的存储位置和清理策略,如在 settings.gradle 文件中设置:
buildCache {
local {
directory = new File(rootDir, 'build-cache')
removeUnusedEntriesAfterDays = 30
}
}
此外,合理配置 Gradle 的 JVM 参数也能提高构建性能。可以根据机器的内存情况,为 Gradle 分配足够的内存,如在 gradle.properties 文件中设置org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 ,为 Gradle 进程分配 4GB 的堆内存和 512MB 的元空间内存。
功能测试
升级完成后,进行全面的功能测试是必不可少的环节。这就像是新车试驾,只有经过实际测试,才能确保车辆的各项性能符合要求。由于 AGP 9.0 带来了诸多变化,这些变化可能会对应用的功能产生影响,因此需要通过功能测试来验证应用的各项功能是否正常。
可以编写单元测试、集成测试和 UI 自动化测试用例,覆盖应用的各个功能模块。例如,对于一个电商应用,需要测试商品浏览、添加购物车、支付等功能是否正常。在 Android Studio 中,可以使用 JUnit、Espresso 等测试框架来编写测试用例。对于单元测试,可以在 src/test/java 目录下创建测试类,使用 JUnit 来测试业务逻辑代码。对于 UI 自动化测试,可以在 src/androidTest/java 目录下创建测试类,使用 Espresso 来测试应用的界面交互功能。
同时,还可以利用持续集成工具,如 Jenkins、GitLab CI/CD 等,将测试集成到构建流程中,每次代码提交后自动触发测试,及时发现问题。比如,在 GitLab CI/CD 中,可以配置如下的.gitlab-ci.yml 文件:
image: openjdk:11
stages:
- build
- test
build:
stage: build
script:
- ./gradlew assembleDebug
test:
stage: test
script:
- ./gradlew testDebug connectedCheck
这样,每次代码提交到 GitLab 仓库时,都会自动触发构建和测试任务,确保应用的质量。