Android Gradle Plugin 9.0 发布,为什么这会是个史诗级大坑版本

0 阅读6分钟

最近的 AGP 9.0 更新,可以说使用又炸出来了一批大坑,对于 Android 可能还好,但是对于 KMP 等用户来说,可能接近“天塌了”的情况,那么这次 AGP 更新了什么?其实大家关心的可能主要就三核心大变动:

  • 彻底切到“新 DSL 接口”,旧实现和旧 Variant API 被无情切割
  • 内置 Kotlin(Built-in Kotlin)
  • KMP plugin 不再能和 com.android.application / com.android.library 同模块共存

newDsl

首先就是 newDsl,AGP 9.0 开始只暴露新的 public DSL interfaces,旧的 DSL 类型(比如历史上很多插件/脚本会强转到 BaseExtension 之类)不再提供,并且旧的 variant API(applicationVariants 等)也一起被切断,要迁移到 androidComponents API 。

从 Google 的角度会是,终于可以在主版本把“历史兼容层”拔掉了,扔掉了一个历史包袱,但是对于用户来说,可能会有:

  • 自己写的 Gradle 插件或 buildSrc 里强转旧类型直接崩
  • 依赖 applicationVariantslibraryVariants 的逻辑要改成 androidComponents

当然,也不是完全就不行了,官方也提供了 android.newDsl=false 回退,但 AGP 10 会移除这个退路,所以虽然可以过渡,但是适配是必然的。

包括使用了 Hilt、 KSP 的老版本的都可能会无法编译,只能说过渡期还是有的,但是如果对应的插件作者不玩了,那就只能你自己过渡了。

Built-in Kotlin

Built-in Kotlin 现在默认开启,AGP 9.0 会默认启用 built-in Kotlin:目标就是你不需要再给 Android module 单独 apply 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,将 android.kotlinOptions{} DSL 迁移到 kotlin.compilerOptions{}
  • 迁移 kotlin.sourceSets{} DSL ,另外可以使用 variant API 的 addStaticSourceDirectoryaddGeneratedSourceDirectory 方法:

除此之外,而且 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 禁用 built-in Kotlin

KMP

最后就是 KMP ,KMP plugin 不再能和 com.android.application / com.android.library 同模块共存,composeApp 必须拆 androidApp 才能工作。

对于 KMP,使用 AGP 9.0+ 时,KMP Gradle plugin 不再兼容 com.android.application / com.android.library 同模块,而解决方案就是:

把 Android 入口(Activity/Application 等)抽到单独的 Android app module,共享代码 module 改成使用新的 Android-KMP library plugin:com.android.kotlin.multiplatform.library

也就是:

  • androidApp:纯 Android Application 模块(打包 APK 的入口在这里)
  • composeApp:共享代码模块(变成 Android Library,并进一步迁移到 Google 的 Android-KMP library plugin)

更准确说法是:

  • 把 Android Application 的那部分(Activity/Application/manifest/打包配置) 提出去
  • shared module 仍然可以保留 androidMain source set 放 Android-only 的共享实现,但它只能是“library 语义”,不能再同时承担 application plugin 的职责

简单来说主要有:

  • 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 重命名为 mainandroidApp 是一个普通 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 相关依赖

  • IDE Run Configuration 里,把以前的 composeApp 改成 androidApp

当然,目前还能暂时用 android.enableLegacyVariantApi=true 苟住,但这个“legacy”也会在 AGP 10 被彻底移除,同时记得 android.newDsl=false

另外, Android-KMP library plugin 对 shared module 的 Android 部分不提供传统的 variant-aware 依赖配置(debugImplementation/releaseImplementation 等会失效),因此需要改用其提供的 classpath/配置方式来注入依赖:

  • 你以前在 shared module 用 debugImplementation / releaseImplementation 或者依赖 buildType/flavor 来注入不同实现/依赖 ,就需要搬家
  • 很多只在 debug 才需要的东西(tooling、调试依赖、某些开关)在 shared module 没法按 variant 优雅区分,只能换策略

实际上我记得,KMP 早期就是推荐 Shared Module + Android App Module ,后来为了简化上手难度,特别是 JetBrains 的 Wizard 向导,推广了 Single Module (Umbrella) 模式,可以一个模块既是共享库又是安卓应用,现在又是一个轮回。

另外,Android Studio 需要 Otter 3 Feature Drop 2025.2.3 才支持 AGP 9.0.0,IntelliJ IDEA 预计要 2026 年第一季度。

为什么

为什么会有“大变动”? 这大概是谷歌想把“把多年技术债一次性结账”,说人话就是:维护成本太高了,开猿节流,把上面三点合起来,你会发现 AGP 9 的目标非常一致:

  • 控制公共 API 面:旧 DSL/旧 Variant API 太开放,导致外部插件、buildSrc、脚本到处用内部实现,AGP 团队成本 UP
  • 为更稳定的构建模型铺路:Gradle 9 的大方向之一是把 Configuration Cache 推成“首选执行模式”,并逐步淘汰不兼容的老 API/模式,AGP 9 的“新 DSL + 新 Variant API(androidComponents)”正好是顺势而为
  • Kotlin 集成:把 Kotlin 编译集成进 AGP(built-in Kotlin),减少过去 KGP/AGP 之间的“脆弱耦合点”

所以可以看到 ,AGP 9 就像是 Google 在做断舍离,所以对于开发者来说,也会是一个被动断舍离的过程,不过话又说回来,不更新就不会有问题,能苟则苟果然还是真理,谁知道过几个版本是不是又抽回来了呢?

所以,你试了 AGP 9 了吗?

参考链接

developer.android.com/build/relea…

developer.android.com/build/migra…

kotlinlang.org/docs/multip…

developer.android.com/build/migra…