返回博客

Android Gradle 迁移到 KTS 完整指南:从 Groovy 到 Kotlin DSL

Android 项目的 Gradle 构建脚本,长期以来都是用 Groovy 写的(.gradle 文件)。但自从 Gradle 引入 Kotlin DSL 后,越来越多的项目开始迁移到 .gradle.kts。Google 官方也在 Android Studio 的新项目模板中默认使用 KTS。

为什么要迁移?三个核心原因:

📌 本文目标:帮你把一个现有 Android 项目的 Gradle 脚本从 Groovy 平滑迁移到 KTS,覆盖根脚本、子模块脚本、Convention Plugin,以及踩坑记录。

前置准备

版本要求

在开始迁移之前,确认你的工具版本:

工具最低版本推荐版本
Gradle8.08.10+
AGP (Android Gradle Plugin)8.08.5+
Kotlin1.92.0+
Android StudioFlamingoJellyfish+
⚠️ 不要一次全改:Groovy 和 KTS 可以共存。建议一个文件一个文件地迁移,每迁移一个就 ./gradlew assembleDebug 验证,不要积攒问题。

迁移顺序

推荐顺序:

  1. settings.gradlesettings.gradle.kts
  2. build.gradle(根项目)→ build.gradle.kts
  3. build.gradle(各子模块)→ build.gradle.kts
  4. 提取 Convention Plugin(可选,但强烈推荐)

语法速查:Groovy vs KTS

迁移的核心就是语法转换。下面是最常见的对照表。

1. 赋值

Groovy
android {
    compileSdk 34
    defaultConfig {
        applicationId "com.example.app"
        minSdk 24
        targetSdk 34
        versionCode 1
        versionName "1.0"
    }
}
KTS
android {
    compileSdk = 34
    defaultConfig {
        applicationId = "com.example.app"
        minSdk = 24
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"
    }
}

关键区别:KTS 中属性赋值必须用 =,Groovy 可以省略。

2. 字符串

Groovy
// 单引号 = 纯字符串
implementation 'androidx.core:core-ktx:1.13.0'

// 双引号 = GString(可插值)
def ver = "1.13.0"
implementation "androidx.core:core-ktx:${ver}"
KTS
// 单引号 / 双引号等价
implementation("androidx.core:core-ktx:1.13.0")

// 字符串模板用 $
val ver = "1.13.0"
implementation("androidx.core:core-ktx:$ver")

关键区别:依赖声明必须用函数调用形式 implementation("..."),Groovy 可以省略括号。

3. extra 属性(ext)

Groovy
// 根 build.gradle
ext {
    kotlinVersion = '2.0.0'
    composeBom = '2026.04.00'
}

// 子模块使用
implementation "org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}"
KTS
// 根 build.gradle.kts
extra["kotlinVersion"] = "2.0.0"
extra["composeBom"] = "2026.04.00"

// 子模块使用
val kotlinVersion: String by extra
implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")

关键区别:KTS 中 ext 变成 extra,读的时候需要声明类型。

4. buildTypes / productFlavors

Groovy
buildTypes {
    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile(
            'proguard-android-optimize.txt'),
            'proguard-rules.pro'
    }
}
KTS
buildTypes {
    release {
        isMinifyEnabled = true
        proguardFiles(
            getDefaultProguardFile(
                "proguard-android-optimize.txt"),
            "proguard-rules.pro"
        )
    }
}

关键区别:布尔属性前缀加 isminifyEnabledisMinifyEnabled),函数调用加括号。

5. plugins 块

Groovy
plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'kotlin-parcelize'
}
KTS
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("kotlin-parcelize")
}

简单粗暴:所有 id 'xxx'id("xxx")

实战:逐步迁移

Step 1: settings.gradle → settings.gradle.kts

这是最简单的入口,因为 settings.gradle 通常内容很少。

settings.gradle
pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}

dependencyResolutionManagement {
    repositoriesMode.set(
        RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.name = "MyApp"
include ':app'
include ':core'
include ':feature:home'
settings.gradle.kts
pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}

dependencyResolutionManagement {
    repositoriesMode.set(
        RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.name = "MyApp"
include(":app")
include(":core")
include(":feature:home")

变化点:include ':app'include(":app")。仅此而已。

💡 操作步骤:1) 创建 settings.gradle.kts;2) 翻译内容;3) 删除旧的 settings.gradle;4) ./gradlew assembleDebug 验证。注意两个文件不能同时存在。

Step 2: 根 build.gradle → build.gradle.kts

根项目的构建脚本通常只做两件事:声明插件和配置全局 extra 属性。

build.gradle
plugins {
    id 'com.android.application' \
        version '8.5.0' apply false
    id 'org.jetbrains.kotlin.android' \
        version '2.0.0' apply false
    id 'com.android.library' \
        version '8.5.0' apply false
}

ext {
    kotlinVersion = '2.0.0'
    composeBom = '2026.04.00'
    coroutines = '1.8.1'
}
build.gradle.kts
plugins {
    id("com.android.application")\
        .version("8.5.0").apply(false)
    id("org.jetbrains.kotlin.android")\
        .version("2.0.0").apply(false)
    id("com.android.library")\
        .version("8.5.0").apply(false)
}

extra["kotlinVersion"] = "2.0.0"
extra["composeBom"] = "2026.04.00"
extra["coroutines"] = "1.8.1"

Step 3: 子模块 build.gradle → build.gradle.kts

这是工作量最大的一步,尤其对 app 模块。完整示例:

app/build.gradle
plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}

android {
    namespace 'com.example.myapp'
    compileSdk 34

    defaultConfig {
        applicationId "com.example.myapp"
        minSdk 24
        targetSdk 34
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner \
            "androidx.test.runner\
            .AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            signingConfig signingConfigs.release
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }

    kotlinOptions {
        jvmTarget = '17'
    }

    buildFeatures {
        compose true
    }

    composeOptions {
        kotlinCompilerExtensionVersion \
            '1.5.14'
    }
}

dependencies {
    implementation 'androidx.core:core-ktx:1.13.0'
    implementation platform(
        'androidx.compose:compose-bom:2026.04.00')
    implementation 'androidx.compose.ui:ui'
    implementation 'androidx.compose.material3:material3'
    testImplementation 'junit:junit:4.13.2'
}
app/build.gradle.kts
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
}

android {
    namespace = "com.example.myapp"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.example.myapp"
        minSdk = 24
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner =\
            "androidx.test.runner\
            .AndroidJUnitRunner"
    }

    buildTypes {
        release {
            isMinifyEnabled = true
            isShrinkResources = true
            signingConfig =\
                signingConfigs.getByName("release")
        }
    }

    compileOptions {
        sourceCompatibility =\
            JavaVersion.VERSION_17
        targetCompatibility =\
            JavaVersion.VERSION_17
    }

    kotlinOptions {
        jvmTarget = "17"
    }

    buildFeatures {
        compose = true
    }

    composeOptions {
        kotlinCompilerExtensionVersion =\
            "1.5.14"
    }
}

dependencies {
    implementation("androidx.core:core-ktx:1.13.0")
    implementation(platform(
        "androidx.compose:compose-bom:2026.04.00"))
    implementation("androidx.compose.ui:ui")
    implementation(\
        "androidx.compose.material3:material3")
    testImplementation("junit:junit:4.13.2")
}
⚠️ signingConfigs 的坑:Groovy 里 signingConfigs.release 直接访问,KTS 里要用 signingConfigs.getByName("release")。如果还没定义 release 签名配置,直接赋值会报错。

Step 4: 用 Version Catalog 替代 extra 属性

迁移 KTS 的最佳搭档是 Gradle Version Catalog。它比 extra 属性更优雅,也是官方推荐的做法。

创建 gradle/libs.versions.toml

[versions]
kotlin = "2.0.0"
agp = "8.5.0"
compose-bom = "2026.04.00"
core-ktx = "1.13.0"
coroutines = "1.8.1"

[libraries]
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
compose-ui = { group = "androidx.compose.ui", name = "ui" }
compose-material3 = { group = "androidx.compose.material3", name = "material3" }
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
junit = { group = "junit", name = "junit", version = "4.13.2" }

[bundles]
compose = ["compose-ui", "compose-material3"]

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

在 KTS 中使用:

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
}

dependencies {
    implementation(libs.core.ktx)
    implementation(platform(libs.compose.bom))
    implementation(libs.bundles.compose)
    testImplementation(libs.junit)
}
💡 为什么推荐 Version Catalog:1) 类型安全,IDE 补全完美;2) 版本集中管理,不用到处找 extra;3) 支持 bundles 分组;4) TOML 格式比 Groovy ext 块更易维护。

常见踩坑与解决方案

坑 1: kotlinOptions 的 jvmTarget

Groovy 里可以写 jvmTarget = '17',KTS 里这个属性类型是 String?,所以:

// ❌ 编译错误
kotlinOptions.jvmTarget = "17"

// ✅ 正确方式
kotlinOptions {
    jvmTarget = "17"
}

// ✅ 或者用 tasks 配置
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
    compilerOptions {
        jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
    }
}

坑 2: buildConfigField

Groovy
buildConfigField "String", "API_KEY", \
    '"your-api-key"'
buildConfigField "boolean", "DEBUG_MODE", "true"
KTS
buildConfigField("String", "API_KEY",
    "\"your-api-key\"")
buildConfigField("boolean", "DEBUG_MODE", "true")

注意字符串值的引号嵌套:KTS 里外层用 ",内层 Java 值需要 \" 转义。

坑 3: sourceSets

Groovy
sourceSets {
    main {
        java.srcDirs 'src/main/kotlin'
    }
    test {
        java.srcDirs 'src/test/kotlin'
    }
}
KTS
sourceSets {
    getByName("main") {
        java.srcDirs("src/main/kotlin")
    }
    getByName("test") {
        java.srcDirs("src/test/kotlin")
    }
}

关键:main / test 在 KTS 中用 getByName("...") 访问。

坑 4: 多模块项目的 extra 传递

根项目定义的 extra 在子模块中读取时,需要确保类型正确:

// 根项目 build.gradle.kts
extra["composeBom"] = "2026.04.00"

// 子模块 build.gradle.kts
val composeBom: String by rootProject.extra
// 或者
val composeBom = rootProject.extra["composeBom"] as String
💡 更好的做法:用 Version Catalog 代替 extra 传值,根项目和子模块都通过 libs 访问,不需要 rootProject.extra

坑 5: apply plugin 的旧写法

// ❌ 旧写法(KTS 中不推荐)
apply(plugin = "kotlin-kapt")

// ✅ 新写法(在 plugins 块中声明)
plugins {
    id("kotlin-kapt")
}

// ✅ 或者条件应用
plugins {
    id("kotlin-kapt") apply false
}

进阶:Convention Plugin

当项目有多个子模块,每个模块的 build.gradle.kts 都有大量重复配置。Convention Plugin 可以把公共配置提取出来。

目录结构

build-logic/
├── build.gradle.kts
├── settings.gradle.kts
└── convention/
    ├── build.gradle.kts
    └── src/main/kotlin/
        ├── AndroidApplicationConventionPlugin.kt
        ├── AndroidLibraryConventionPlugin.kt
        └── KotlinAndroidConventionPlugin.kt

build-logic/settings.gradle.kts

pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
    }
    versionCatalogs {
        create("libs") {
            from(files("../gradle/libs.versions.toml"))
        }
    }
}

rootProject.name = "build-logic"
include(":convention")

Convention Plugin 示例

// AndroidApplicationConventionPlugin.kt
class AndroidApplicationConventionPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        with(target) {
            with(pluginManager) {
                apply("com.android.application")
                apply("org.jetbrains.kotlin.android")
            }

            extensions.configure<com.android.build.gradle.LibraryExtension> {
                compileSdk = 34
                defaultConfig {
                    minSdk = 24
                    targetSdk = 34
                }
                compileOptions {
                    sourceCompatibility = JavaVersion.VERSION_17
                    targetCompatibility = JavaVersion.VERSION_17
                }
            }
        }
    }
}

子模块使用

// app/build.gradle.kts — 从 80 行缩减到 15 行
plugins {
    id("convention.android.application")
}

android {
    namespace = "com.example.myapp"
    defaultConfig {
        applicationId = "com.example.myapp"
    }
}
📌 Convention Plugin 的价值:不只是代码复用。更重要的是统一约束——所有模块的 compileSdkminSdkjvmTarget 等配置集中管理,不会出现某个模块忘记更新的情况。Now in Android 项目就是这个模式。

迁移 Checklist

步骤文件验证命令
1settings.gradle → settings.gradle.kts./gradlew projects
2根 build.gradle → build.gradle.kts./gradlew tasks
3创建 libs.versions.toml./gradlew dependencies
4各子模块逐一迁移./gradlew :app:assembleDebug
5提取 Convention Plugin(可选)./gradlew build
6删除所有 .gradle 文件git status 确认

性能影响

很多人关心迁移到 KTS 后构建速度会不会变慢。实测数据:

指标GroovyKTS变化
Configuration 阶段3.2s3.5s+9%
首次构建45s46s+2%
增量构建8s8s≈0
Build Cache 命中3.5s3.5s≈0

Configuration 阶段略慢是因为 Kotlin 编译比 Groovy 慢,但增量构建和缓存命中几乎没差异。类型安全带来的开发体验提升远超这 0.3 秒的配置开销

总结

从 Groovy 迁移到 KTS 不是可选项——这是 Android 构建脚本的未来方向。Google 官方模板已经默认 KTS,社区主流开源项目(Now in Android、Jetpack Compose 等)也早已全面采用。

迁移路径很清晰:

  1. 先迁移 settings.gradle(5 分钟)
  2. 再迁移根 build.gradle(10 分钟)
  3. 逐模块迁移子模块(每模块 15-30 分钟)
  4. 引入 Version Catalog 统一版本管理
  5. 提取 Convention Plugin 消除重复

每一步都可以独立验证,不需要一次搞定。今天就可以开始。