Android 构建中的 Java 版本

无论您的源代码是用 Java、Kotlin 还是两者兼而有之编写的,您都必须在多个地方为您的构建选择 JDK 或 Java 语言版本。

Overview of JDK relationships in a Gradle build
图 1. 构建中的 JDK 关系

词汇表

Java 开发工具包 (JDK)
Java 开发工具包 (JDK) 包含
  • 工具,例如编译器、性能分析器和归档创建器。这些工具在构建过程中在后台用于创建您的应用。
  • 包含您可以从 Kotlin 或 Java 源代码调用的 API 的库。请注意,并非所有函数都可在 Android 上使用。
  • Java 虚拟机 (JVM),一个执行 Java 应用程序的解释器。您可以使用 JVM 运行 Android Studio IDE 和 Gradle 构建工具。JVM 不在 Android 设备或模拟器上使用。
JetBrains Runtime (JBR)
JetBrains Runtime (JBR) 是一个增强型 JDK,随 Android Studio 分发。它包含多项优化,可用于 Studio 和相关的 JetBrains 产品,但也可用于运行其他 Java 应用程序。

如何选择用于运行 Android Studio 的 JDK?

我们建议您使用 JBR 来运行 Android Studio。它随 Android Studio 一起部署并用于测试 Android Studio,包含多项优化,可实现最佳的 Android Studio 使用体验。为此,请勿设置 STUDIO_JDK 环境变量。

Android Studio 的启动脚本按以下顺序查找 JVM:

  1. STUDIO_JDK 环境变量
  2. studio.jdk 目录(在 Android Studio 分发中)
  3. jbr 目录 (JetBrains Runtime),在 Android Studio 分发中。推荐。
  4. JDK_HOME 环境变量
  5. JAVA_HOME 环境变量
  6. PATH 环境变量中的 java 可执行文件

如何选择运行 Gradle 构建的 JDK?

如果您使用 Android Studio 中的按钮运行 Gradle,则会使用 Android Studio 设置中配置的 JDK 来运行 Gradle。如果您在终端中(无论是在 Android Studio 内部还是外部)运行 Gradle,则 JAVA_HOME 环境变量(如果已设置)会确定哪个 JDK 运行 Gradle 脚本。如果未设置 JAVA_HOME,则它会使用 PATH 环境变量中的 java 命令。

为获得最一致的结果,请确保您设置了 JAVA_HOME 环境变量,并将 Android Studio 中的 Gradle JDK 配置设置为同一 JDK。

在运行构建时,Gradle 会创建一个名为 daemon 的进程来执行实际构建。只要构建使用相同的 JDK 和 Gradle 版本,此进程就可以重复使用。重复使用守护进程可缩短启动新 JVM 和初始化构建系统所需的时间。

如果您使用不同的 JDK 或 Gradle 版本启动构建,则会创建更多守护进程,从而消耗更多 CPU 和内存。

Android Studio 中的 Gradle JDK 配置

如需修改现有项目的 Gradle JDK 配置,请从 File(在 macOS 上为 Android Studio> Settings > Build, Execution, Deployment > Build Tools > Gradle 中打开 Gradle 设置。Gradle JDK 下拉菜单中包含以下选项供您选择:

  • 宏,例如 JAVA_HOMEGRADLE_LOCAL_JAVA_HOME
  • JDK 表项,采用 vendor-version 格式,例如 jbr-17,存储在 Android 配置文件
  • 下载 JDK
  • 添加特定 JDK
  • 从操作系统的默认 JDK 安装目录本地检测到的 JDK

选定的选项存储在项目的 .idea/gradle.xml 文件中的 gradleJvm 选项中,并且其 JDK 路径解析用于通过 Android Studio 启动时运行 Gradle。

图 2. Android Studio 中的 Gradle JDK 设置。

宏支持动态选择项目 JDK 路径:

  • JAVA_HOME:使用同名环境变量
  • GRADLE_LOCAL_JAVA_HOME:使用 .gradle/config.properties 文件中的 java.home 属性,该属性默认为 JetBrains Runtime。

选定的 JDK 用于运行您的 Gradle 构建,并在编辑构建脚本和源代码时解析 JDK API 引用。请注意,指定的 compileSdk 将进一步限制在编辑和构建源代码时可用的 Java 符号。

请务必选择高于或等于您在 Gradle 构建中使用的插件所用 JDK 版本的 JDK 版本。如需确定 Android Gradle 插件 (AGP) 所需的最低 JDK 版本,请参阅版本说明中的兼容性表。

例如,Android Gradle 插件版本 8.x 需要 JDK 17。如果您尝试使用早期 JDK 版本运行使用它的 Gradle 构建,它会报告如下消息:

An exception occurred applying plugin request [id: 'com.android.application']
> Failed to apply plugin 'com.android.internal.application'.
   > Android Gradle plugin requires Java 17 to run. You are currently using Java 11.
      Your current JDK is located in /usr/local/buildtools/java/jdk
      You can try some of the following options:
       - changing the IDE settings.
       - changing the JAVA_HOME environment variable.
       - changing `org.gradle.java.home` in `gradle.properties`.

我的 Java 或 Kotlin 源代码可以使用哪些 Java API?

Android 应用程序可以使用 JDK 中定义的部分 API,但并非全部。Android SDK 定义了许多 Java 库函数的实现,作为其可用 API 的一部分。compileSdk 属性指定了在编译 Kotlin 或 Java 源代码时要使用的 Android SDK 版本。

Kotlin

android {
    ...
    compileSdk = 33
}

Groovy

android {
    ...
    compileSdk 33
}

每个 Android 版本都支持特定版本的 JDK 及其可用 Java API 的子集。如果您使用的 Java API 在 compileSdk 中可用,但在指定的 minSdk 中不可用,您或许可以通过称为脱糖的过程在早期 Android 版本中使用该 API。如需了解支持的 API,请参阅通过脱糖可用的 Java 11+ API

使用此表可确定每个 Android API 支持的 Java 版本,以及在哪里可以找到有关哪些 Java API 可用的详细信息。

Android Java 支持的 API 和语言特性
14 (API 34) 17 核心库
13 (API 33) 11 核心库
12 (API 32) 11 Java API
11 及更低版本 Android 版本

哪个 JDK 编译我的 Java 源代码?

Java 工具链 JDK 包含用于编译任何 Java 源代码的 Java 编译器。此 JDK 还在构建期间运行 javadoc 和单元测试。

工具链默认使用用于运行 Gradle 的 JDK。如果您使用默认设置并在不同的机器(例如,您的本地机器和单独的持续集成服务器)上运行构建,则如果使用了不同的 JDK 版本,您的构建结果可能会有所不同。

为了创建更一致的构建,您可以明确指定 Java 工具链版本。指定此项可:

  • 在运行构建的系统上找到兼容的 JDK。
    • 如果不存在兼容的 JDK(并且已定义工具链解析器),则下载一个。
  • 公开工具链 Java API 以供从源代码调用。
  • 使用其 Java 语言版本编译 Java 源代码。
  • 提供 sourceCompatibilitytargetCompatibility 的默认值。

我们建议您始终指定 Java 工具链,并确保已安装指定的 JDK,或向您的构建中添加工具链解析器

无论您的源代码是用 Java、Kotlin 还是两者兼而有之编写的,您都可以指定工具链。在模块的 build.gradle(.kts) 文件的顶层指定工具链。

按如下方式指定 Java 工具链版本:

Kotlin

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

Groovy

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

无论您的源代码是 Kotlin、Java 还是两者的混合,此方法都适用。

工具链 JDK 版本可以与用于运行 Gradle 的 JDK 相同,但请记住它们服务于不同的目的。

我的 Java 源代码可以使用哪些 Java 语言源特性?

sourceCompatibility 属性确定在编译 Java 源代码时可用的 Java 语言特性。它不影响 Kotlin 源代码。

在模块的 build.gradle(.kts) 文件中按如下方式指定 sourceCompatibility

Kotlin

android {
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
    }
}

Groovy

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
    }
}

如果未指定,此属性默认为 Java 工具链版本。如果您未使用 Java 工具链,则它默认为 Android Gradle 插件选择的版本(例如,Java 8 或更高版本)。

编译 Kotlin 或 Java 源代码时可以使用哪些 Java 二进制特性?

targetCompatibilityjvmTarget 属性分别确定在为编译后的 Java 和 Kotlin 源代码生成字节码时使用的 Java 类格式版本。

某些 Kotlin 特性在添加等效的 Java 特性之前就已存在。早期 Kotlin 编译器必须创建自己的方式来表示这些 Kotlin 特性。其中一些特性后来被添加到 Java 中。随着 jvmTarget 级别更高,Kotlin 编译器可能会直接使用 Java 特性,这可能会带来更好的性能。

不同版本的 Android 支持不同版本的 Java。您可以通过提高 targetCompatibilityjvmTarget 来利用其他 Java 特性,但这可能也会迫使您提高最低 Android SDK 版本,以确保该特性可用。

请注意,targetCompatibility 必须大于或等于 sourceCompatibility。实际上,sourceCompatibilitytargetCompatibilityjvmTarget 通常应使用相同的值。您可以按如下方式进行设置:

Kotlin

android {
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }
    kotlinOptions {
        jvmTarget = "17"
    }
}

Groovy

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }
    kotlinOptions {
        jvmTarget '17'
    }
}

如果未指定,这些属性默认为 Java 工具链版本。如果您未使用 Java 工具链,则默认值可能会有所不同并导致构建问题。因此,我们建议您始终明确指定这些值或使用 Java 工具链