d8

d8 是一种命令行工具,Android Studio 和 Android Gradle 插件使用它将项目的 Java 字节码编译成可在 Android 设备上运行的 DEX 字节码。d8 允许您在应用代码中使用 Java 8 语言特性。

d8 也作为独立工具包含在 Android Build Tools 28.0.1 及更高版本中:android_sdk/build-tools/version/

一般用法

d8 只需要您想要转换为 DEX 字节码的已编译 Java 字节码的路径。例如

d8 MyProject/app/build/intermediates/classes/debug/*/*.class

输入字节码可以是 *.class 文件或 JAR、APK 或 ZIP 文件等容器的任意组合。您还可以包含 DEX 文件,以便 d8 合并到 DEX 输出中,这在包含增量构建的输出时很有用。

默认情况下,d8 将 Java 字节码编译为优化的 DEX 文件,并包含调试信息,您可以在运行时使用这些信息调试代码。但是,您可以包含可选标志来执行增量构建,指定应编译到主 DEX 文件中的类,并指定使用 Java 8 语言特性所需的其他资源的路径。

d8 path-to-input-files [options]

下表描述了可与 d8 一起使用的可选标志

选项 说明
--debug

编译 DEX 字节码以包含调试信息,例如调试符号表。

此选项默认启用。要在 DEX 字节码中包含调试信息,d8 要求输入 Java 字节码包含该信息。例如,如果您使用 javac 编译代码,则需要传递 -g 标志以在输出 Java 字节码中包含调试信息。

为应用或库的发布版本编译 DEX 文件时,请改用 --release 标志。

--release

编译不带调试信息的 DEX 字节码。但是,d8 包含一些在生成堆栈跟踪和记录异常时使用的信息。

为公开发布编译字节码时传递此标志。

--output path

指定 DEX 输出的所需路径。默认情况下,d8 将 DEX 文件输出到当前工作目录中。

如果您指定 ZIP 或 JAR 文件的路径和名称,d8 会创建指定的文件并包含输出 DEX 文件。如果您指定现有目录的路径,d8 会将 DEX 文件输出到该目录中。

--lib android_sdk/platforms/api-level/android.jar 指定 Android SDK 的 android.jar 路径。当编译使用 Java 8 语言特性的字节码时,此标志是必需的。
--classpath path 指定 d8 编译项目 DEX 文件可能需要的类路径资源。特别是,当编译使用 Java 8 语言特性的字节码时,d8 要求您指定某些资源。
--min-api number 指定您希望输出 DEX 文件支持的最低 API 级别。
--intermediate 传递此标志可让 d8 知道您没有编译项目的完整 Java 字节码集。此标志在执行增量构建时很有用。与编译您期望在设备上运行的优化 DEX 文件不同,d8 会创建中间 DEX 文件并将它们存储在指定的输出或默认路径中。

当您要编译要在设备上运行的 DEX 文件时,请排除此标志并将中间 DEX 类的路径指定为输入。

--file-per-class

将每个类编译成单独的 DEX 文件。

启用此标志可让您通过仅重新编译已更改的类来执行更多增量构建。使用 Android Gradle 插件执行增量构建时,此优化默认启用。

您不能在指定 --main-dex-list 的同时使用此标志。

--no-desugaring 禁用 Java 8 语言特性。仅当您不打算编译使用 Java 8 语言特性的 Java 字节码时才使用此标志。
--main-dex-list path

指定一个文本文件,其中列出 d8 应包含在主 DEX 文件中的类,通常名为 classes.dex。当您不使用此标志指定类列表时,d8 不保证哪些类会包含在主 DEX 文件中。

由于 Android 系统在启动应用时首先加载主 DEX 文件,因此您可以使用此标志通过将某些类编译到主 DEX 文件中来在启动时优先处理它们。当支持旧版 multidex 时,这尤其有用,因为只有主 DEX 文件中的类在加载旧版 multidex 库之前是可用的。

请记住,每个 DEX 文件仍必须满足64K 引用限制。因此,不要为主 DEX 文件指定过多的类,否则您会遇到编译错误。默认情况下,当使用 --main-dex-list 指定类时,d8 仅将这些类包含在主 DEX 文件中。这是为了使与主 DEX 文件中缺少类相关的问题更容易调试。如果您指定 --release 模式,d8 会尝试通过将尽可能多的其他类包含在主 DEX 文件中直至达到 64K 限制,从而减少打包到应用发布版本中的 DEX 文件数量。

您不能在指定 --file-per-class 的同时使用此标志。

--pg-map file 使用 file 作为分发映射文件。
--file-per-class-file

为每个输入的 .class 文件生成一个单独的 DEX 文件。

将合成类与其原始类放在一起。

--desugared-lib file

指定脱糖库配置。

file 是 JSON 格式的脱糖库配置文件。

--main-dex-rules file 用于放置在主 DEX 文件中的类的 Proguard keep 规则。
--main-dex-list-output file 将生成的主 DEX 列表输出到文件.

--force-enable-assertions [:class_or_package_name...]

--force-ea [:class_or_package_name...]

强制启用 javac 生成的断言代码。

--force-disable-assertions [:class_or_package_name...]

--force-da [:class_or_package_name...]

强制禁用 javac 生成的断言代码。这是生成 DEX 文件时 javac 断言代码的默认处理方式。

--force-passthrough-assertions [:class_or_package_name...]

--force-pa [:class_or_package_name...]

不更改 javac 生成的断言代码。这是生成 class 文件时 javac 断言代码的默认处理方式。

--force-assertions-handler:handler method [:class_or_package_name...]

--force-ah:handler method [:class_or_package_name...]

更改 javackotlinc 生成的断言代码,使其在每次断言错误时调用方法 handler method,而不是抛出错误。handler method 指定为类名后跟一个点和方法名。处理方法必须接受一个 java.lang.Throwable 类型的参数且返回类型为 void
--thread-count number of threads 指定用于编译的线程数。如果未指定,则线程数根据启发式方法(考虑核心数)确定。
--map-diagnostics[ :type] from-level to-level 将报告为 from-leveltype(默认任何)诊断映射到 to-level,其中 from-levelto-level 是 'info'、'warning' 或 'error' 之一,可选的 type 是诊断的简单或完全限定的 Java 类型名称。如果未指定 type,则映射 from-level 处的所有诊断。请注意,致命编译器错误无法映射。
--version 打印您当前使用的 d8 版本。
--help 打印使用 d8 的帮助文本。

执行增量构建

为了提高开发期间的构建速度,例如用于持续集成构建,指示 d8 仅编译项目 Java 字节码的子集。例如,如果启用按类 dexing,则只能重新编译自上次构建以来已修改的类。

以下命令对少量类执行增量构建并启用按类 dexing。该命令还指定了增量构建的输出目录。

d8 MainActivity.class R.class --intermediate --file-per-class --output ~/build/intermediate/dex

d8 执行增量构建时,它会将额外信息存储在 DEX 输出中。d8 稍后使用该信息来正确处理 --main-dex-list 选项并在应用的完整构建期间合并 DEX 文件。

例如,在处理 Java 8 lambda 类时,d8 会跟踪为每个输入类创建了哪些 lambda 类。在完整构建期间,当 d8 将类包含在主 DEX 文件中时,它会查阅元数据以确保为该类创建的所有 lambda 类也包含在主 DEX 文件中。

如果您已经通过多次增量构建将项目的所有字节码编译为 DEX 文件,请通过将中间 DEX 文件的目录传递给 d8 来执行完整构建,如以下命令所示。此外,您可以使用 --main-dex-list 指定要 d8 编译到主 DEX 文件中的类。由于输入是一组已经编译成 DEX 字节码的文件,因此此构建应该比干净构建更快完成。

d8 ~/build/intermediate/dex --release --main-dex-list ~/build/classes.txt --output ~/build/release/dex

编译使用 Java 8 语言特性的字节码

d8 允许您通过称为脱糖的编译过程在代码中使用 Java 8 语言特性。脱糖将这些有用的语言特性转换为可在 Android 平台上运行的字节码。

Android Studio 和 Android Gradle 插件包含 d8 为您启用脱糖所需的类路径资源。但是,当从命令行使用 d8 时,您需要自己包含它们。

其中一个资源是目标 Android SDK 中的 android.jar。此资源包含一组 Android 平台 API。使用 --lib 标志指定其路径。

另一个资源是编译到您项目中的 Java 字节码集,这些字节码当前未编译为 DEX 字节码,但需要它们来编译其他类到 DEX 字节码。

例如,如果您的代码使用 默认和静态接口方法(它们是 Java 8 语言特性),则您需要使用此标志指定项目所有 Java 字节码的路径,即使您不打算将所有字节码编译为 DEX 字节码。这是因为 d8 需要此信息才能理解您的项目代码并解析对接口方法的调用。

以下代码示例对访问默认接口方法的类执行增量构建

d8 MainActivity.class --intermediate --file-per-class --output ~/build/intermediate/dex
--lib android_sdk/platforms/api-level/android.jar
--classpath ~/build/javac/debug