将构建配置从 Groovy 迁移到 Kotlin

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

相较于 Groovy,Kotlin 更受青睐用于编写 Gradle 脚本,因为它更具可读性,并提供更好的编译时检查和 IDE 支持。

尽管 Kotlin 目前在 Android Studio 的代码编辑器中提供了比 Groovy 更好的集成度,但使用 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 上而不是在 project.rootDir 上调用 toString()。要获取根目录的值,请使用花括号将 ${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 中,只有调试和发布构建类型是隐式可用的。所有其他自定义构建类型都必须手动创建。

在 Groovy 中,您可以直接使用调试、发布和其他某些构建类型,而无需先创建它们。以下代码段展示了 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 插件的一部分:

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

您可以在 Google Maven 仓库中找到完整的插件列表。

Kotlin 插件可以通过多个插件 ID 引用。我们建议使用带命名空间的插件 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 插件门户Maven Central 仓库Google 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 代码示例,请参阅以下文档页面:

已知问题

目前存在一个已知问题,即使用 Kotlin 的构建速度可能比使用 Groovy 慢。

如何报告问题

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

更多资源

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