mcpField
K
kotlin-tooling-agp9-migration

kotlin-tooling-agp9-migration

github.com/kotlin开发1.1k2.2k 次安装7 周前更新
代码仓库

关于 kotlin-tooling-agp9-migration

面向 Kotlin Multiplatform 项目升级到 Android Gradle Plugin 9.0 的迁移技能。它分析模块配置,并指导库迁移、应用拆分及多平台项目重组。

主要功能

  1. 01分析 Gradle 模块与配置
  2. 02指导 KMP Android 库迁移
  3. 03拆分 Android 应用与共享模块
  4. 04重组多平台入口模块
  5. 05检查 AGP 9.0 插件兼容性

适用场景

  1. 01将 KMP 项目升级到 AGP 9.0
  2. 02从共享代码中拆出 Android 应用
  3. 03迁移 Android 库模块
  4. 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.md
references/
DSL-REFERENCE.md
KNOWN-ISSUES.md
MIGRATION-APP-SPLIT.md
MIGRATION-FULL-RESTRUCTURE.md
MIGRATION-LIBRARY.md
PLUGIN-COMPATIBILITY.md
VERSION-MATRIX.md
scripts/
analyze-project.sh

详细介绍

KMP AGP 9.0 迁移

Android Gradle Plugin 9.0 会导致 Android 应用和库插件与同一模块中的 Kotlin Multiplatform 插件不兼容。本指南将引导你完成迁移。

步骤 0:分析项目

在进行任何更改之前,先了解项目结构:

  1. 阅读 settings.gradle.kts(或 .gradle)以找出所有模块
  2. 对于每个模块,阅读其 build.gradle.kts,确定应用了哪些插件
  3. 检查项目是否使用 Gradle 版本目录(gradle/libs.versions.toml)。如果存在,阅读其中的当前 AGP/Gradle/Kotlin 版本。如果不存在,则直接在 build.gradle.kts 文件中查找版本(通常位于根目录的 buildscript {} 或 plugins {} 块中)。据此调整本指南中的所有示例——版本目录示例使用 alias(libs.plugins.xxx),直接使用时则使用 id("plugin.id") version "x.y.z"
  4. 阅读 gradle/wrapper/gradle-wrapper.properties 以查看 Gradle 版本
  5. 检查 gradle.properties 中是否有现有的变通设置(android.enableLegacyVariantApi)
  6. 检查是否使用了 org.jetbrains.kotlin.android 插件——AGP 9.0 内置 Kotlin,必须移除此插件
  7. 检查是否使用了 org.jetbrains.kotlin.kapt 插件——它与内置 Kotlin 不兼容,必须迁移到 KSP 或 com.android.legacy-kapt
  8. 检查是否有可能与 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。

摘要:

  1. 替换插件:com.android.library → com.android.kotlin.multiplatform.library
  2. 移除 org.jetbrains.kotlin.android 插件(如果存在;AGP 9.0 已内置 Kotlin 支持)
  3. 迁移 DSL:将配置从顶层 android {} 块移至 kotlin { android {} }:
    kotlin {
        android {
            namespace = "com.example.lib"
            compileSdk = 35
            minSdk = 24
        }
    }
    
  4. 重命名源代码目录(仅当模块使用经典 Android 布局,而非 KMP 布局时):
    • src/main → src/androidMain
    • src/test → src/androidHostTest
    • src/androidTest → src/androidDeviceTest
    • 如果模块已使用 src/androidMain/,则无需重命名目录
  5. 移动依赖项:将顶层 dependencies {} 中的依赖项移至 sourceSets:
    kotlin {
        sourceSets {
            androidMain.dependencies {
                implementation("androidx.appcompat:appcompat:1.7.0")
            }
        }
    }
    
  6. 显式启用资源:如果模块使用 Android 或 Compose Multiplatform 资源:
    kotlin {
        android {
            androidResources { enable = true }
        }
    }
    
  7. 启用 Java 编译:如果模块包含 .java 源文件:
    kotlin {
        android {
            withJava()
        }
    }
    
  8. 显式启用测试:如果模块包含单元测试或插桩测试:
    kotlin {
        android {
            withHostTest { isIncludeAndroidResources = true }
            withDeviceTest {
                instrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
            }
        }
    }
    
  9. 更新 Compose 工具依赖项:
    // Old: 
    debugImplementation(libs.androidx.compose.ui.tooling)
    // New:
    androidRuntimeClasspath(libs.androidx.compose.ui.tooling)
    
  10. 显式发布消费者 ProGuard 规则(如果适用):
    kotlin {
        android {
            consumerProguardFiles.add(file("consumer-rules.pro"))
        }
    }
    
  11. 解决子依赖变体(产品风味 / 构建类型): 由于新的 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。

摘要:

  1. 创建 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)
    }
    
  2. 移动 Android 入口点代码:从 src/androidMain/ 移至 androidApp/src/main/:
    • MainActivity.kt(以及其他任何 Activity / Fragment)
    • AndroidManifest.xml(应用级清单,包含 <application> 和启动用的 <activity>)——请确认 <activity> 上的 android:name 使用其新位置对应的完全限定类名
    • Android Application 类(如果存在)
    • 应用级资源(启动器图标、主题等)
  3. 添加到 settings.gradle.kts:include(":androidApp")
  4. 添加到根目录的 build.gradle.kts:插件声明使用 apply false
  5. 转换原模块:按照路径 A 的步骤,将其从应用转换为库
  6. 确保命名空间不同:应用模块和库模块必须使用不同的命名空间
  7. 从共享模块中移除:applicationId、targetSdk、versionCode、versionName
  8. 更新 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)
└── ...
步骤
  1. 先执行路径 B — 提取 androidApp(AGP 9.0 的强制要求)
  2. 提取 desktopApp(如果存在桌面目标):
    • 创建模块,使用 org.jetbrains.compose 和 application {} 插件
    • 将 main() 函数从 desktopMain 移至 desktopApp/src/main/kotlin/
    • 将 compose.desktop { application { ... } } 配置移至 desktopApp/build.gradle.kts
    • 添加对 shared 模块的依赖
  3. 提取 webApp(如果存在 wasmJs/js 目标):
    • 创建模块,并采用相应的 Kotlin/JS 或 Kotlin/Wasm 配置
    • 将 Web 入口点从 wasmJsMain/jsMain 移至 webApp/src/wasmJsMain/kotlin/
    • 将浏览器/分发配置移至 webApp/build.gradle.kts
    • 添加对 shared 模块的依赖
  4. iOS — 通常已位于单独的 iosApp 目录中。请确认:
    • Framework 导出配置(binaries.framework)仍保留在 shared 模块中
    • Xcode 项目引用了正确的 Framework 路径
  5. 将模块从 composeApp 重命名为 shared:
    • 重命名目录
    • 更新 settings.gradle.kts 中的 include
    • 更新各模块中的所有依赖引用
  6. 清理 shared 模块:移除所有已移至平台 App 模块的平台入口点代码和 App 专用配置
变体:原生 UI

如果某些平台使用原生 UI(例如 iOS 使用 SwiftUI),请将 shared 拆分为:

  • sharedLogic — 供所有平台使用的业务逻辑
  • sharedUI — 仅供使用共享 UI 的平台使用的 Compose Multiplatform UI
变体:服务端

如果项目包含服务端目标:

  • 在根目录添加 server 模块
  • 将所有客户端模块移至 app/ 目录下
  • 添加 core 模块,用于存放服务端与客户端共享的代码(模型、验证)

版本更新

无论采用哪种迁移路径,都必须进行以下更新:

  1. Gradle wrapper — 更新至 9.1.0+:

    # gradle/wrapper/gradle-wrapper.properties
    distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
    
  2. 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
    }
    
  3. JDK — 确保使用 JDK 17+(AGP 9.0 的要求)

  4. SDK Build Tools — 更新至 36.0.0:

    Install via SDK Manager or configure in android { buildToolsVersion = "36.0.0" }
    
  5. 检查 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")
}
按模块迁移策略

对于大型项目,请逐个模块迁移:

  1. 全局禁用:在 gradle.properties 中设置 android.builtInKotlin=false
  2. 对已迁移的模块应用 opt-in 插件以启用:
    plugins {
        id("com.android.built-in-kotlin") version "AGP_VERSION"
    }
    
  3. 对该模块执行步骤 1-4
  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.uniquePackageNamesfalsetrue确保每个库都有唯一的命名空间
android.enableAppCompileTimeRClassfalsetrue将对 R 字段的 switch 重构为 if/else
android.defaults.buildfeatures.resvaluestruefalse在需要的地方启用 resValues = true
android.defaults.buildfeatures.shaderstruefalse在需要的地方启用 shaders
android.r8.optimizedResourceShrinkingfalsetrue检查 R8 keep 规则
android.r8.strictFullModeForKeepRulesfalsetrue更新 keep 规则,明确指定所需内容
android.proguard.failOnMissingFilesfalsetrue移除无效的 ProGuard 文件引用
android.r8.proguardAndroidTxt.disallowedfalsetrue仅使用 proguard-android-optimize.txt
android.r8.globalOptionsInConsumerRules.disallowedfalsetrue从库的 consumer 规则中移除全局选项
android.sourceset.disallowProviderfalsetrue在 androidComponents 上使用 Sources API
android.sdk.defaultTargetSdkToCompileSdkIfUnsetfalsetrue显式指定 targetSdk
android.onlyEnableUnitTestForTheTestedBuildTypefalsetrue仅在测试非默认构建类型时使用

检查并移除现在会导致错误的属性:

  • android.r8.integratedResourceShrinking — 已移除,始终启用
  • android.enableNewResourceShrinker.preciseShrinking — 已移除,始终启用

纯 Android 提示

对于升级到 AGP 9.0 的非 KMP Android 模块,请按照上文“内置 Kotlin 迁移”中的步骤操作, 然后检查“Gradle 属性默认值变更”表。其他变更:

  • 检查新的 DSL 接口 — BaseExtension 已移除;请使用 CommonExtension 或特定的扩展类型
  • Java 默认版本已从 Java 8 更改为 Java 11 — 确保 compileOptions 与之相符

验证

迁移后,请使用检查清单进行验证。重点检查:

  1. ./gradlew build 成功运行且没有错误
  2. 所有平台目标均成功构建(Android、通过 xcodebuild 构建的 iOS、Desktop、JS/Wasm)
  3. ./gradlew :shared:allTests 和 Android 单元测试通过
  4. KMP 模块中没有 com.android.library 或 com.android.application
  5. AGP 9.0 模块中没有 org.jetbrains.kotlin.android
  6. 源集使用正确的名称(androidMain、androidHostTest、androidDeviceTest)
  7. 没有关于变体 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 {} 扩展辅助方法已过时

参考文件

评论

支持 Markdown