将您的构建配置从 Groovy 迁移到 Kotlin

Android Gradle 插件 4.0 添加了对在 Gradle 构建配置中使用 Kotlin 的支持,以替代传统上在 Gradle 配置文件中使用的编程语言 Groovy。

与 Groovy 相比,Kotlin 更适合编写 Gradle 脚本,因为 Kotlin 可读性更好,并提供更好的编译时检查和 IDE 支持。

尽管与 Groovy 相比,Kotlin 目前在 Android Studio 的代码编辑器中提供了更好的集成,但使用 Kotlin 的构建往往比使用 Groovy 的构建速度慢,因此在决定是否迁移时请考虑构建性能。

此页面提供了有关将 Android 应用的 Gradle 构建文件从 Groovy 转换为 Kotlin 的基本信息。有关更全面的迁移指南,请参阅 Gradle 的 官方文档

时间线

从 Android Studio Giraffe 开始,新项目默认使用 Kotlin DSL (build.gradle.kts) 进行构建配置。这比使用 Groovy DSL (build.gradle) 提供了更好的编辑体验,具有语法高亮显示、代码补全和导航到声明的功能。要了解更多信息,请参阅 Gradle Kotlin DSL 入门指南

常用术语

Kotlin DSL: 主要指 Android Gradle 插件 Kotlin DSL,或者偶尔指 底层的 Gradle Kotlin DSL

在本迁移指南中,“Kotlin”和“Kotlin DSL”可以互换使用。同样,“Groovy”和“Groovy DSL”也可以互换使用。

脚本文件命名

脚本文件扩展名基于构建文件所使用的语言。

  • 用 Groovy 编写的 Gradle 构建文件使用 .gradle 文件名扩展名。
  • 用 Kotlin 编写的 Gradle 构建文件使用 .gradle.kts 文件名扩展名。

转换语法

Groovy 和 Kotlin 之间的语法存在一些差异,因此您需要在构建脚本中应用这些更改。

向方法调用添加括号

Groovy 允许您省略方法调用中的括号,而 Kotlin 则需要括号。要迁移您的配置,请向这些方法调用添加括号。此代码显示如何在 Groovy 中配置设置

compileSdkVersion 30

这是用 Kotlin 编写的相同代码

compileSdkVersion(30)

向赋值调用添加 =

Groovy DSL 允许您在分配属性时省略赋值运算符 =,而 Kotlin 则需要它。此代码显示如何在 Groovy 中分配属性

java {
    sourceCompatibility JavaVersion.VERSION_17
    targetCompatibility JavaVersion.VERSION_17
}

此代码显示如何在 Kotlin 中分配属性

java {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
}

转换字符串

以下是 Groovy 和 Kotlin 之间的字符串差异

  • 字符串的双引号:虽然 Groovy 允许使用单引号定义字符串,但 Kotlin 则需要双引号。
  • 点表达式中的字符串插值:在 Groovy 中,对于点表达式的字符串插值,你只需要使用 $ 前缀即可,但 Kotlin 要求你将点表达式用花括号括起来。例如,在 Groovy 中,你可以使用 $project.rootDir,如下所示

        myRootDirectory = "$project.rootDir/tools/proguard-rules-debug.pro"
        

    然而,在 Kotlin 中,上述代码会对 project 调用 toString() 方法,而不是对 project.rootDir 调用。要获取根目录的值,请将 ${project.rootDir} 表达式用花括号括起来。

        myRootDirectory = "${project.rootDir}/tools/proguard-rules-debug.pro"
        

    要了解更多信息,请参阅 Kotlin 文档中的字符串模板

重命名文件扩展名

在迁移构建文件内容时,为每个构建文件追加 .kts。例如,选择一个构建文件,例如 settings.gradle 文件。将文件重命名为 settings.gradle.kts,并将文件内容转换为 Kotlin。确保在迁移每个构建文件后,你的项目仍然可以编译。

先迁移你最小的文件,积累经验,然后再继续迁移。你可以在一个项目中混合使用 Kotlin 和 Groovy 构建文件,所以请花时间仔细进行迁移。

def 替换为 valvar

def 替换为 valvar,这是在 Kotlin 中定义变量的方式。这是 Groovy 中的变量声明。

def building64Bit = false

这是用 Kotlin 编写的相同代码

val building64Bit = false

在布尔属性前添加前缀 is

Groovy 使用基于属性名称的属性推断逻辑。对于布尔属性 foo,其 *推断方法* 可以是 getFoosetFooisFoo。因此,一旦转换为 Kotlin,你需要将属性名称更改为 Kotlin 不支持的推断方法。例如,对于 buildTypes DSL 布尔元素,你需要在它们前面添加前缀 is。这段代码展示了如何在 Groovy 中设置布尔属性。

android {
    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            ...
        }
        debug {
            debuggable true
            ...
        }
    ...

以下是 Kotlin 中的相同代码。请注意,属性前缀为 is

android {
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            isShrinkResources = true
            ...
        }
        getByName("debug") {
            isDebuggable = true
            ...
        }
    ...

转换列表和映射

Groovy 和 Kotlin 中的列表和映射使用不同的语法定义。Groovy 使用 [],而 Kotlin 使用 listOfmapOf 显式调用集合创建方法。迁移时,请确保将 [] 替换为 listOfmapOf

以下是 Groovy 和 Kotlin 中定义列表的方法

jvmOptions += ["-Xms4000m", "-Xmx4000m", "-XX:+HeapDumpOnOutOfMemoryError</code>"]

这是用 Kotlin 编写的相同代码

jvmOptions += listOf("-Xms4000m", "-Xmx4000m", "-XX:+HeapDumpOnOutOfMemoryError")

以下是 Groovy 和 Kotlin 中定义映射的方法

def myMap = [key1: 'value1', key2: 'value2']

这是用 Kotlin 编写的相同代码

val myMap = mapOf("key1" to "value1", "key2" to "value2")

配置构建类型

在 Kotlin DSL 中,只有 debug 和 release 构建类型是隐式可用的。所有其他自定义构建类型必须手动创建。

在 Groovy 中,你可以使用 debug、release 和某些其他构建类型,而无需事先创建它们。以下代码片段展示了在 Groovy 中使用 debugreleasebenchmark 构建类型的配置。

buildTypes {
 debug {
   ...
 }
 release {
   ...
 }
 benchmark {
   ...
 }
}

要在 Kotlin 中创建等效的配置,你必须显式创建 benchmark 构建类型。

buildTypes {
 debug {
   ...
 }

 release {
   ...
 }
 register("benchmark") {
    ...
 }
}

从 buildscript 块迁移到 plugins 块

如果你的构建使用buildscript {} 块向项目添加插件,则应重构为改用plugins {} 块。plugins {} 块使应用插件更容易,并且与版本目录配合良好。

此外,当你在构建文件中使用 plugins {} 块时,即使构建失败,Android Studio 也知道上下文。此上下文有助于修复 Kotlin DSL 文件,因为它允许 Studio IDE 执行代码补全并提供其他有用的建议。

查找插件 ID

buildscript {} 块使用插件的Maven 坐标(例如 com.android.tools.build:gradle:7.4.0)将插件添加到构建类路径,而 plugins {} 块则使用插件 ID。

对于大多数插件,插件 ID 是使用 apply plugin 应用插件时使用的字符串。例如,以下插件 ID 是 Android Gradle Plugin 的一部分:

  • com.android.application
  • com.android.library
  • com.android.lint
  • com.android.test

你可以在Google Maven 存储库中找到完整的插件列表。

Kotlin 插件可以通过多个插件 ID 来引用。我们建议使用命名空间插件 ID,并根据下表将简写形式重构为命名空间插件 ID。

简写插件 ID 命名空间插件 ID
kotlin org.jetbrains.kotlin.jvm
kotlin-android org.jetbrains.kotlin.android
kotlin-kapt org.jetbrains.kotlin.kapt
kotlin-parcelize org.jetbrains.kotlin.plugin.parcelize

你也可以在Gradle Plugin PortalMaven Central RepositoryGoogle Maven 存储库上搜索插件。阅读开发自定义 Gradle 插件以了解有关插件 ID 如何工作的更多信息。

执行重构

一旦你知道了你使用的插件的 ID,请执行以下步骤:

  1. 如果你的 buildscript {} 块中仍然声明了插件的存储库,请将其移动到settings.gradle 文件。

  2. 将插件添加到顶级 build.gradle 文件中的 plugins {} 块中。你需要在此处指定插件的 ID 和版本。如果插件不需要应用于根项目,请使用 apply false

  3. 从顶级 build.gradle.kts 文件中删除 classpath 条目。

  4. 通过将插件添加到模块级 build.gradle 文件中的 plugins {} 块中来应用插件。你只需要在此处指定插件的 ID,因为版本是从根项目继承的。

  5. 从模块级 build.gradle 文件中删除插件的 apply plugin 调用。

例如,此设置使用 buildscript {} 块。

// Top-level build.gradle file
buildscript {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
    dependencies {
        classpath("com.android.tools.build:gradle:7.4.0")
        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0")
        ...
    }
}

// Module-level build.gradle file
apply(plugin: "com.android.application")
apply(plugin: "kotlin-android")

这是使用 plugins {} 块的等效设置。

// Top-level build.gradle file
plugins {
   id 'com.android.application' version '7.4.0' apply false
   id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
   ...
}

// Module-level build.gradle file
plugins {
   id 'com.android.application'
   id 'org.jetbrains.kotlin.android'
   ...
}

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

转换 plugins 块

plugins {} 块应用插件在 Groovy 和 Kotlin 中类似。以下代码展示了在使用版本目录时如何在 Groovy 中应用插件。

// Top-level build.gradle file
plugins {
   alias libs.plugins.android.application apply false
   ...
}

// Module-level build.gradle file
plugins {
   alias libs.plugins.android.application
   ...
}

以下代码展示了如何在 Kotlin 中执行相同的操作。

// Top-level build.gradle.kts file
plugins {
   alias(libs.plugins.android.application) apply false
   ...
}

// Module-level build.gradle.kts file
plugins {
   alias(libs.plugins.android.application)
   ...
}

以下代码展示了在*不*使用版本目录时如何在 Groovy 中应用插件。

// Top-level build.gradle file
plugins {
   id 'com.android.application' version '7.3.0' apply false
   ...
}

// Module-level build.gradle file
plugins {
   id 'com.android.application'
   ...
}

以下代码展示了如何在 Kotlin 中执行相同的操作。

// Top-level build.gradle.kts file
plugins {
   id("com.android.application") version "7.3.0" apply false
   ...
}

// Module-level build.gradle.kts file
plugins {
   id("com.android.application")
   ...
}

有关 plugins {} 块的更多详细信息,请参阅 Gradle 文档中的应用插件

其他

有关其他功能的 Kotlin 代码示例,请参阅以下文档页面:

已知问题

目前,一个已知问题是,与 Groovy 相比,Kotlin 的构建速度可能会更慢。

如何报告问题

有关如何提供我们需要的信息来分类你的问题的信息,请参阅构建工具和 Gradle 错误的详细信息。然后,使用 Google 的公共问题跟踪器提交错误。

更多资源

有关用 Kotlin 编写的 Gradle 构建文件的实际示例,请参阅 GitHub 上的Now In Android 示例应用