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 字节码的完整集合。此标志在执行增量构建时非常有用。 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 文件中的类,主 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 将生成的主 DEX 列表输出到file.

--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 字节码的子集。例如,如果您启用了每个类的 DEX 处理,则可以仅重新编译自上次构建以来已修改的类。

以下命令执行少量类的增量构建并启用每个类的 DEX 处理。该命令还为增量构建指定了输出目录。

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