关于 kotlin-tooling-agp9-migration
面向 Kotlin Multiplatform 项目升级到 Android Gradle Plugin 9.0 的迁移技能。它分析模块配置,并指导库迁移、应用拆分及多平台项目重组。
主要功能
- 01分析 Gradle 模块与配置
- 02指导 KMP Android 库迁移
- 03拆分 Android 应用与共享模块
- 04重组多平台入口模块
- 05检查 AGP 9.0 插件兼容性
适用场景
- 01将 KMP 项目升级到 AGP 9.0
- 02从共享代码中拆出 Android 应用
- 03迁移 Android 库模块
- 04重组多平台 Compose 项目
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ kotlin/ kotlin-agent-skills --skill kotlin-tooling-agp9-migration安装后在 Claude Code 中输入/kotlin-tooling-agp9-migration确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/checklist.mdreferences/DSL-REFERENCE.mdKNOWN-ISSUES.mdMIGRATION-APP-SPLIT.mdMIGRATION-FULL-RESTRUCTURE.mdMIGRATION-LIBRARY.mdPLUGIN-COMPATIBILITY.mdVERSION-MATRIX.mdscripts/analyze-project.sh详细介绍
KMP AGP 9.0 迁移
Android Gradle Plugin 9.0 会导致 Android 应用和库插件与同一模块中的 Kotlin Multiplatform 插件不兼容。本指南将引导你完成迁移。
步骤 0:分析项目
在进行任何更改之前,先了解项目结构:
- 阅读
settings.gradle.kts(或.gradle)以找出所有模块 - 对于每个模块,阅读其
build.gradle.kts,确定应用了哪些插件 - 检查项目是否使用 Gradle 版本目录(
gradle/libs.versions.toml)。如果存在,阅读其中的当前 AGP/Gradle/Kotlin 版本。如果不存在,则直接在build.gradle.kts文件中查找版本(通常位于根目录的buildscript {}或plugins {}块中)。据此调整本指南中的所有示例——版本目录示例使用alias(libs.plugins.xxx),直接使用时则使用id("plugin.id") version "x.y.z" - 阅读
gradle/wrapper/gradle-wrapper.properties以查看 Gradle 版本 - 检查
gradle.properties中是否有现有的变通设置(android.enableLegacyVariantApi) - 检查是否使用了
org.jetbrains.kotlin.android插件——AGP 9.0 内置 Kotlin,必须移除此插件 - 检查是否使用了
org.jetbrains.kotlin.kapt插件——它与内置 Kotlin 不兼容,必须迁移到 KSP 或com.android.legacy-kapt - 检查是否有可能与 AGP 9.0 不兼容的第三方插件(见下文“插件兼容性”部分)
如果可以使用 Bash,请从此技能所在目录运行 scripts/analyze-project.sh,以获取结构化摘要。
对各模块进行分类
对于每个模块,确定其类型:
| 当前插件 | 迁移路径 |
|---|---|
kotlin.multiplatform + com.android.library | 路径 A — 替换库插件 |
kotlin.multiplatform + com.android.application | 路径 B — 必须拆分 Android |
kotlin.multiplatform,且一个模块中有多个平台入口点 | 路径 C — 完全重构(推荐) |
com.android.application 或 com.android.library(无 KMP) | 参见下文“纯 Android 提示” |
确定范围
- 对于任何组合使用 KMP 和 Android 应用插件的模块,必须采用路径 B
- 当项目有一个单体的
composeApp(或类似)模块,且其中包含多个平台(Android、桌面、Web)的入口点时,建议采用路径 C。这符合 JetBrains 新的默认项目结构,即每个平台都有自己的应用模块。 - 询问用户是只采用路径 B(最低限度的必要迁移),还是采用路径 C(推荐的完整重构)
路径 A:库模块迁移
当模块应用 kotlin.multiplatform + com.android.library 时使用此路径。
完整的迁移前后代码请参阅 references/MIGRATION-LIBRARY.md。
摘要:
- 替换插件:
com.android.library→com.android.kotlin.multiplatform.library - 移除
org.jetbrains.kotlin.android插件(如果存在;AGP 9.0 已内置 Kotlin 支持) - 迁移 DSL:将配置从顶层
android {}块移至kotlin { android {} }:kotlin { android { namespace = "com.example.lib" compileSdk = 35 minSdk = 24 } } - 重命名源代码目录(仅当模块使用经典 Android 布局,而非 KMP 布局时):
src/main→src/androidMainsrc/test→src/androidHostTestsrc/androidTest→src/androidDeviceTest- 如果模块已使用
src/androidMain/,则无需重命名目录
- 移动依赖项:将顶层
dependencies {}中的依赖项移至sourceSets:kotlin { sourceSets { androidMain.dependencies { implementation("androidx.appcompat:appcompat:1.7.0") } } } - 显式启用资源:如果模块使用 Android 或 Compose Multiplatform 资源:
kotlin { android { androidResources { enable = true } } } - 启用 Java 编译:如果模块包含
.java源文件:kotlin { android { withJava() } } - 显式启用测试:如果模块包含单元测试或插桩测试:
kotlin { android { withHostTest { isIncludeAndroidResources = true } withDeviceTest { instrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } } } - 更新 Compose 工具依赖项:
// Old: debugImplementation(libs.androidx.compose.ui.tooling) // New: androidRuntimeClasspath(libs.androidx.compose.ui.tooling) - 显式发布消费者 ProGuard 规则(如果适用):
kotlin { android { consumerProguardFiles.add(file("consumer-rules.pro")) } } - 解决子依赖变体(产品风味 / 构建类型):
由于新的 KMP Android 库插件采用单变体架构,因此无法原生处理发布多个变体的依赖项(例如
debug/release构建类型,或free/paid等产品风味)。请使用localDependencySelection配置回退行为:kotlin { android { localDependencySelection { // 确定从 Android 库依赖项中使用哪种构建类型,并按优先级排序 selectBuildTypeFrom.set(listOf("debug", "release")) // 如果依赖项有 'tier' 维度,则选择 'free' 风味 productFlavorDimension("tier") { selectFrom.set(listOf("free")) } } } }
路径 B:拆分 Android 应用与共享模块
当模块应用 kotlin.multiplatform + com.android.application 时使用此路径。此操作是兼容 AGP 9.0 的强制要求。
完整指南请参阅 references/MIGRATION-APP-SPLIT.md。
摘要:
- 创建
androidApp模块,并为其添加独立的build.gradle.kts:plugins { alias(libs.plugins.androidApplication) // Do NOT apply kotlin-android — AGP 9.0 includes Kotlin support alias(libs.plugins.composeMultiplatform) // if using Compose alias(libs.plugins.composeCompiler) // if using Compose } android { namespace = "com.example.app" compileSdk = 35 defaultConfig { applicationId = "com.example.app" minSdk = 24 targetSdk = 35 versionCode = 1 versionName = "1.0" } buildFeatures { compose = true } } dependencies { implementation(projects.shared) // or whatever the shared module is named implementation(libs.androidx.activity.compose) } - 移动 Android 入口点代码:从
src/androidMain/移至androidApp/src/main/:MainActivity.kt(以及其他任何 Activity / Fragment)AndroidManifest.xml(应用级清单,包含<application>和启动用的<activity>)——请确认<activity>上的android:name使用其新位置对应的完全限定类名- Android Application 类(如果存在)
- 应用级资源(启动器图标、主题等)
- 添加到
settings.gradle.kts:include(":androidApp") - 添加到根目录的
build.gradle.kts:插件声明使用apply false - 转换原模块:按照路径 A 的步骤,将其从应用转换为库
- 确保命名空间不同:应用模块和库模块必须使用不同的命名空间
- 从共享模块中移除:
applicationId、targetSdk、versionCode、versionName - 更新 IDE 运行配置:将模块从旧模块改为
androidApp
路径 C:全面重构(推荐)
当项目采用单体模块(通常是 composeApp),其中包含多个平台的入口点时使用此路径。此步骤为可选,但符合 JetBrains 新的默认方案。
完整指南请参阅 references/MIGRATION-FULL-RESTRUCTURE.md。
目标结构
project/
├── shared/ ← KMP library (was composeApp), pure shared code
├── androidApp/ ← Android entry point only
├── desktopApp/ ← Desktop entry point only (if desktop target exists)
├── webApp/ ← Wasm/JS entry point only (if web target exists)
├── iosApp/ ← iOS Xcode project (usually already separate)
└── ...
步骤
- 先执行路径 B — 提取
androidApp(AGP 9.0 的强制要求) - 提取
desktopApp(如果存在桌面目标):- 创建模块,使用
org.jetbrains.compose和application {}插件 - 将
main()函数从desktopMain移至desktopApp/src/main/kotlin/ - 将
compose.desktop { application { ... } }配置移至desktopApp/build.gradle.kts - 添加对
shared模块的依赖
- 创建模块,使用
- 提取
webApp(如果存在 wasmJs/js 目标):- 创建模块,并采用相应的 Kotlin/JS 或 Kotlin/Wasm 配置
- 将 Web 入口点从
wasmJsMain/jsMain移至webApp/src/wasmJsMain/kotlin/ - 将浏览器/分发配置移至
webApp/build.gradle.kts - 添加对
shared模块的依赖
- iOS — 通常已位于单独的
iosApp目录中。请确认:- Framework 导出配置(
binaries.framework)仍保留在shared模块中 - Xcode 项目引用了正确的 Framework 路径
- Framework 导出配置(
- 将模块从
composeApp重命名为shared:- 重命名目录
- 更新
settings.gradle.kts中的 include - 更新各模块中的所有依赖引用
- 清理 shared 模块:移除所有已移至平台 App 模块的平台入口点代码和 App 专用配置
变体:原生 UI
如果某些平台使用原生 UI(例如 iOS 使用 SwiftUI),请将 shared 拆分为:
sharedLogic— 供所有平台使用的业务逻辑sharedUI— 仅供使用共享 UI 的平台使用的 Compose Multiplatform UI
变体:服务端
如果项目包含服务端目标:
- 在根目录添加
server模块 - 将所有客户端模块移至
app/目录下 - 添加
core模块,用于存放服务端与客户端共享的代码(模型、验证)
版本更新
无论采用哪种迁移路径,都必须进行以下更新:
-
Gradle wrapper — 更新至 9.1.0+:
# gradle/wrapper/gradle-wrapper.properties distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip -
AGP 版本 — 更新至 9.0.0+,并添加 KMP 库插件。
使用版本目录(
gradle/libs.versions.toml)时:[versions] agp = "9.0.1" [plugins] android-kotlin-multiplatform-library = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" }不使用版本目录时 — 更新
com.android.*插件版本,并在根目录的build.gradle.kts中添加:plugins { id("com.android.application") version "9.0.1" apply false id("com.android.kotlin.multiplatform.library") version "9.0.1" apply false } -
JDK — 确保使用 JDK 17+(AGP 9.0 的要求)
-
SDK Build Tools — 更新至 36.0.0:
Install via SDK Manager or configure in android { buildToolsVersion = "36.0.0" } -
检查 gradle.properties — 移除会导致错误的属性,并检查已更改的默认值(参见“Gradle Properties 默认值变更”一节)
内置 Kotlin 迁移
AGP 9.0 默认对所有 com.android.application 和 com.android.library 模块启用内置 Kotlin 支持。不再需要 org.jetbrains.kotlin.android 插件,应用该插件会导致冲突。
**重要:**内置 Kotlin 不会取代 KMP 支持。KMP 库模块仍需要
org.jetbrains.kotlin.multiplatform + com.android.kotlin.multiplatform.library。
第 1 步:移除 kotlin-android 插件
从所有模块级和根级构建文件中移除:
// Remove from module build.gradle.kts
plugins {
// REMOVE: alias(libs.plugins.kotlin.android)
// REMOVE: id("org.jetbrains.kotlin.android")
}
// Remove from root build.gradle.kts
plugins {
// REMOVE: alias(libs.plugins.kotlin.android) apply false
}
从版本目录(gradle/libs.versions.toml)中移除:
[plugins]
# REMOVE: kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
第 2 步:将 kapt 迁移至 KSP 或 legacy-kapt
org.jetbrains.kotlin.kapt 插件与内置 Kotlin 不兼容。
首选:迁移至 KSP — 请参阅各注解处理器对应的 KSP 迁移指南。
备用方案:使用 com.android.legacy-kapt(版本与 AGP 相同):
# gradle/libs.versions.toml
[plugins]
legacy-kapt = { id = "com.android.legacy-kapt", version.ref = "agp" }
// Module build.gradle.kts — replace kotlin-kapt with legacy-kapt
plugins {
// REMOVE: alias(libs.plugins.kotlin.kapt)
alias(libs.plugins.legacy.kapt)
}
第 3 步:将 kotlinOptions 迁移至 compilerOptions
对于纯 Android 模块(非 KMP),将 android.kotlinOptions {} 迁移至顶层的
kotlin.compilerOptions {}:
// Old
android {
kotlinOptions {
jvmTarget = "11"
languageVersion = "2.0"
freeCompilerArgs += listOf("-Xopt-in=kotlin.RequiresOptIn")
}
}
// New
kotlin {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11)
languageVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
optIn.add("kotlin.RequiresOptIn")
}
}
**注意:**使用内置 Kotlin 时,jvmTarget 默认为 android.compileOptions.targetCompatibility;如果已设置 compileOptions,则可能无需额外设置。
第 4 步:将 kotlin.sourceSets 迁移至 android.sourceSets
使用内置 Kotlin 时,仅支持 android.sourceSets {} 中的 kotlin 集:
// NOT SUPPORTED with built-in Kotlin:
kotlin.sourceSets.named("main") {
kotlin.srcDir("additionalSourceDirectory/kotlin")
}
// Correct:
android.sourceSets.named("main") {
kotlin.directories += "additionalSourceDirectory/kotlin"
}
对于生成的源代码,请使用 Variant API:
androidComponents.onVariants { variant ->
variant.sources.kotlin!!.addStaticSourceDirectory("additionalSourceDirectory/kotlin")
}
按模块迁移策略
对于大型项目,请逐个模块迁移:
- 全局禁用:在
gradle.properties中设置android.builtInKotlin=false - 对已迁移的模块应用 opt-in 插件以启用:
plugins { id("com.android.built-in-kotlin") version "AGP_VERSION" } - 对该模块执行步骤 1-4
- 所有模块迁移完成后,移除
android.builtInKotlin=false和所有com.android.built-in-kotlin插件
可选:为非 Kotlin 模块禁用 Kotlin
对于不包含 Kotlin 源代码的模块,可禁用内置 Kotlin 以节省构建时间:
android {
enableKotlin = false
}
退出(临时措施)
如果受到插件不兼容问题阻碍,可暂时退出:
# gradle.properties
android.builtInKotlin=false
android.newDsl=false # also required if using new DSL opt-out
**警告:**询问用户是否要退出;如果要退出,请提醒他们这只是临时措施。
插件兼容性
完整的兼容性表(包含已知兼容版本、退出标志变通方案和存在问题的插件),请参阅 references/PLUGIN-COMPATIBILITY.md。
迁移前,请列出项目中的所有插件,并逐一对照该表进行检查。如果有任何插件无法正常使用且没有变通方案,请告知用户。如果插件需要退出标志,请将其添加到 gradle.properties,并注明这些是临时变通方案。
Gradle 属性默认值变更
AGP 9.0 更改了许多 Gradle 属性的默认值。请检查 gradle.properties 中是否显式设置了可能因此产生冲突的值。
主要变更:
| 属性 | 旧默认值 | 新默认值 | 操作 |
|---|---|---|---|
android.uniquePackageNames | false | true | 确保每个库都有唯一的命名空间 |
android.enableAppCompileTimeRClass | false | true | 将对 R 字段的 switch 重构为 if/else |
android.defaults.buildfeatures.resvalues | true | false | 在需要的地方启用 resValues = true |
android.defaults.buildfeatures.shaders | true | false | 在需要的地方启用 shaders |
android.r8.optimizedResourceShrinking | false | true | 检查 R8 keep 规则 |
android.r8.strictFullModeForKeepRules | false | true | 更新 keep 规则,明确指定所需内容 |
android.proguard.failOnMissingFiles | false | true | 移除无效的 ProGuard 文件引用 |
android.r8.proguardAndroidTxt.disallowed | false | true | 仅使用 proguard-android-optimize.txt |
android.r8.globalOptionsInConsumerRules.disallowed | false | true | 从库的 consumer 规则中移除全局选项 |
android.sourceset.disallowProvider | false | true | 在 androidComponents 上使用 Sources API |
android.sdk.defaultTargetSdkToCompileSdkIfUnset | false | true | 显式指定 targetSdk |
android.onlyEnableUnitTestForTheTestedBuildType | false | true | 仅在测试非默认构建类型时使用 |
检查并移除现在会导致错误的属性:
android.r8.integratedResourceShrinking— 已移除,始终启用android.enableNewResourceShrinker.preciseShrinking— 已移除,始终启用
纯 Android 提示
对于升级到 AGP 9.0 的非 KMP Android 模块,请按照上文“内置 Kotlin 迁移”中的步骤操作, 然后检查“Gradle 属性默认值变更”表。其他变更:
- 检查新的 DSL 接口 —
BaseExtension已移除;请使用CommonExtension或特定的扩展类型 - Java 默认版本已从 Java 8 更改为 Java 11 — 确保
compileOptions与之相符
验证
迁移后,请使用检查清单进行验证。重点检查:
./gradlew build成功运行且没有错误- 所有平台目标均成功构建(Android、通过
xcodebuild构建的 iOS、Desktop、JS/Wasm) ./gradlew :shared:allTests和 Android 单元测试通过- KMP 模块中没有
com.android.library或com.android.application - AGP 9.0 模块中没有
org.jetbrains.kotlin.android - 源集使用正确的名称(
androidMain、androidHostTest、androidDeviceTest) - 没有关于变体 API 或 DSL 的弃用警告
常见问题
详情请参阅已知问题。重点注意:
KMP 库插件问题
- 库模块中无法使用 BuildConfig——请使用 DI/
AppConfiguration接口,或使用 BuildKonfig 或 gradle-buildconfig-plugin 定义编译时常量 - 没有构建变体——采用单变体架构;编译时常量可以使用 BuildKonfig/gradle-buildconfig-plugin 的 flavors,但特定于变体的依赖项/资源/签名必须移至应用模块
- 不支持 NDK/JNI——请提取到独立的
com.android.library模块 - 未设置
androidResources { enable = true }时,Compose 资源会导致崩溃 - 如果没有在新 DSL 中迁移为
consumerProguardFiles.add(file(...)),Consumer ProGuard 规则会被静默丢弃 - KSP 需要 2.3.1+ 版本才能兼容 AGP 9.0
AGP 9.0 常见问题
- BaseExtension 已移除——使用旧 DSL 类型的约定插件需要重写,改用
CommonExtension - 变体 API 已移除——
applicationVariants、libraryVariants、variantFilter已由androidComponents取代 - 约定插件需要重构——旧的
android {}扩展辅助方法已过时