使用 Jack 编译 (AOSP 6.0 - 8.1)

Jack 是 Android 6.0 - 8.1 的默认 Android 构建工具链

Jack 是一款 Android 工具链,可将 Java 源代码编译为 Android dex 字节码。您无需执行任何其他操作即可使用 Jack - 只需使用标准 makefile 命令来编译树或您的项目。Android 8.1 是使用 Jack 的最后一个版本。

关于 Jack

Jack 的工作原理如图 1 所示。

Jack overview.

图 1. Jack 概览。

Jack 库格式

Jack 有其自己的 .jack 文件格式,其中包含库的预编译 dex 代码,从而实现更快的编译(预 dex)。

Jack library file contents.

图 2. Jack 库文件内容。

Jill

如下图所示,Jill 工具将现有的 .jar 库转换为新的库格式。

Workflow to import an existing `jar.` library.

图 3. 导入现有 .jar 库的工作流程。

Jack 编译服务器

首次使用 Jack 时,它会在您的计算机上启动本地 Jack 编译服务器。此服务器

  • 带来了内在的加速,因为它避免了在每次编译时启动新的主机 JRE JVM、加载 Jack 代码、初始化 Jack 和预热 JIT。它还在小型编译期间(例如,在增量模式下)提供了非常好的编译时间。
  • 是一种短期解决方案,用于控制并行 Jack 编译的数量。服务器避免了计算机过载(内存或磁盘问题),因为它限制了并行编译的数量。

Jack 服务器在没有任何编译的空闲时间后自行关闭。它在 localhost 接口上使用两个 TCP 端口,并且在外部不可用。所有参数(并行编译的数量、超时、端口号等)都可以通过编辑 $HOME/.jack 文件来修改。

$HOME/.jack 文件

$HOME/.jack 文件包含 Jack 服务器变量的以下设置,采用完整的 bash 语法

  • SERVER=true 启用 Jack 的服务器功能。
  • SERVER_PORT_SERVICE=8072 设置服务器用于编译目的的 TCP 端口号。
  • SERVER_PORT_ADMIN=8073 设置服务器用于管理目的的 TCP 端口号。
  • SERVER_COUNT=1 未使用。
  • SERVER_NB_COMPILE=4 设置允许的最大并行编译数。SERVER_TIMEOUT=60 设置服务器在没有任何编译的情况下必须等待多少秒空闲时间后才会自行关闭。SERVER_LOG=${SERVER_LOG:=$SERVER_DIR/jack-$SERVER_PORT_SERVICE.log} 设置写入服务器日志的文件。默认情况下,此变量可以被环境变量覆盖。
  • JACK_VM_COMMAND=${JACK_VM_COMMAND:=java} 设置用于在主机上启动 JVM 的默认命令。默认情况下,此变量可以被环境变量覆盖。

排查 Jack 编译问题

问题 操作
您的计算机在编译期间变得无响应,或者您遇到 Jack 编译失败并显示内存不足错误 通过编辑 $HOME/.jack 并将 SERVER_NB_COMPILE 更改为较低的值,来减少同时进行的 Jack 编译数量。
编译失败,显示无法启动后台服务器 最可能的原因是 TCP 端口已在您的计算机上使用。通过编辑 $HOME/.jackSERVER_PORT_SERVICESERVER_PORT_ADMIN 变量)来更改端口。要解除阻止,请通过编辑 $HOME/.jack 并将 SERVER 更改为 false 来禁用 Jack 编译服务器。遗憾的是,这会显着降低您的编译速度,并可能迫使您使用负载控制(make-l 选项)启动 make -j
编译卡住,没有任何进展 要解除阻止,请使用 jack-admin kill-server)杀死 Jack 后台服务器,然后删除临时目录(/tmp$TMPDIR)的 jack-$USER 中包含的临时目录。

查找 Jack 日志

如果您使用 dist 目标运行了 make 命令,则 Jack 日志位于 $ANDROID_BUILD_TOP/out/dist/logs/jack-server.log。否则,您可以通过运行 jack-admin server-log 找到日志。如果出现可重现的 Jack 故障,您可以通过设置以下变量来获取更详细的日志

export ANDROID_JACK_EXTRA_ARGS="--verbose debug --sanity-checks on -D sched.runner=single-threaded"

使用标准 makefile 命令来编译树(或您的项目)并附加标准输出和错误。要删除详细的构建日志,请运行

unset ANDROID_JACK_EXTRA_ARGS

Jack 限制

默认情况下,Jack 服务器只能由计算机上的一位用户使用。要支持其他用户,请为每个用户选择不同的端口号,并相应地调整 SERVER_NB_COMPILE。您还可以通过在 $HOME/.jack 中设置 SERVER=false 来禁用 Jack 服务器。由于当前的 vm-tests-tf 集成,CTS 编译速度很慢。不支持字节码操作工具(例如 JaCoCo)。

使用 Jack

Jack 支持 Java 编程语言 1.7,并集成了以下附加功能。

预 dex

生成 Jack 库文件时,库的 .dex 将生成并作为预 dex 存储在 .jack 库文件中。编译时,Jack 会重用每个库中的预 dex。所有库都已预 dex。

Jack libraries with pre-dex.

图 4. 带有预 dex 的 Jack 库。

如果在编译中使用了压缩、混淆或重新打包,则 Jack 不会重用库预 dex。

增量编译

增量编译意味着仅重新编译自上次编译以来已修改的组件(及其依赖项)。当更改仅限于一组组件时,增量编译可能比完整编译快得多。

默认情况下,增量编译处于禁用状态(并且在启用压缩、混淆、重新打包或 multidex legacy 时会自动停用)。要启用增量构建,请将以下行添加到要增量构建的项目的 Android.mk 文件中

LOCAL_JACK_ENABLED := incremental

压缩和混淆

Jack 使用 ProGuard 配置文件来启用压缩和混淆。

常用选项包括以下内容

  • @
  • -include
  • -basedirectory
  • -injars
  • -outjars(仅支持 1 个输出 jar)
  • -libraryjars
  • -keep
  • -keepclassmembers
  • -keepclasseswithmembers
  • -keepnames
  • -keepclassmembernames
  • -keepclasseswithmembernames
  • -printseeds

压缩选项包括以下内容

  • -dontshrink

混淆选项包括以下内容

  • -dontobfuscate
  • -printmapping
  • -applymapping
  • -obfuscationdictionary
  • -classobfuscationdictionary
  • -packageobfuscationdictionary
  • -useuniqueclassmembernames
  • -dontusemixedcaseclassnames
  • -keeppackagenames
  • -flattenpackagehierarchy
  • -repackageclasses
  • -keepattributes
  • -adaptclassstrings

忽略的选项包括以下内容

  • -dontoptimize(Jack 不会优化)
  • -dontpreverify(Jack 不会预验证)
  • -skipnonpubliclibraryclasses
  • -dontskipnonpubliclibraryclasses
  • -dontskipnonpubliclibraryclassmembers
  • -keepdirectories
  • -target
  • -forceprocessing
  • -printusage
  • -whyareyoukeeping
  • -optimizations
  • -optimizationpasses
  • -assumenosideeffects
  • -allowaccessmodification
  • -mergeinterfacesaggressively
  • -overloadaggressively
  • -microedition
  • -verbose
  • -dontnote
  • -dontwarn
  • -ignorewarnings
  • -printconfiguration
  • -dump

重新打包

Jack 使用 jarjar 配置文件进行重新打包。虽然 Jack 与“rule”规则类型兼容,但与“zap”或“keep”规则类型不兼容。

Multidex 支持

Jack 提供内置和旧版 multidex 支持。由于 dex 文件限制为 65K 个方法,因此超过 65K 个方法的应用必须拆分为多个 dex 文件。有关更多详细信息,请参阅为超过 64K 个方法的应用启用 multidex