mcpField
K
kotlin-tooling-agp9-migration

kotlin-tooling-agp9-migration

github.com/midudev开发7.0k59 次安装2 周前更新
代码仓库

关于 kotlin-tooling-agp9-migration

这是一项帮助 Kotlin Multiplatform 项目迁移到 Android Gradle Plugin 9.0 的技能,指导开发者分析项目、选择模块迁移路径,并调整所需版本和插件。

主要功能

  1. 01分析模块、插件和版本
  2. 02为各模块选择迁移路径
  3. 03拆分 Android 应用与共享 KMP 代码
  4. 04重构多平台入口点
  5. 05更新 Gradle、AGP、JDK 和构建工具
  6. 06迁移 Kotlin 与 kapt 插件用法

适用场景

  1. 01评估 KMP 项目以准备迁移到 AGP 9.0
  2. 02将 Android 应用从共享模块拆分
  3. 03重构多平台 Compose 项目
  4. 04处理 Kotlin 插件和 kapt 不兼容问题

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/midudev/autoskills --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