d8

d8 是一种命令行工具,Android Studio 和 Android Gradle 插件使用该工具来将项目的 Java 字节码编译为在 Android 设备上运行的 DEX 字节码。d8 支持您在应用的代码中使用 Java 8 语言功能。

d8 还作为独立工具纳入了 Android 构建工具 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 字节码集。此标志在执行增量构建时非常有用。d8 会创建中间 DEX 文件,并将其存储在指定的输出或默认路径中,而不是编译您想在设备上运行的优化 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 时特别有用,因为在加载旧版 multidex 库之前,只有主 DEX 文件中的类在运行时可用。

请注意,该主 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 保留规则。
--main-dex-list-output file 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 是“信息”“警告”和“错误”之一,而可选的 type 是诊断的简单或完全限定的 Java 类型名称。如果未指定 type,则会映射 from-level 上的所有诊断。 请注意,无法映射严重编译器错误。
--version 输出您当前使用的 d8 版本。
--help 输出与使用 d8 相关的帮助文本。

执行增量构建

为了在开发过程中提高构建速度(例如提高持续集成 build 的速度),请指示 d8 仅编译项目的部分 Java 字节码。例如,如果您启用了按类 dexing 处理,则只需重新编译自上次构建以来修改过的类。

以下命令可执行几个类的增量构建,并启用按类 dexing 处理。该命令还可为增量构建指定输出目录。

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

d8 在执行增量构建时,会将一些额外的信息存储在 DEX 输出中。随后在完整构建应用时,d8 会利用这些信息来正确处理 --main-dex-list 选项以及合并 DEX 文件。

例如,d8 在处理 Java 8 lambda 类时,会记录为每个输入类创建的 lamdba 类。在执行完整构建的过程中,当 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 字节码的路径,即使您不打算将所有 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