使用 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 概览。

图 1. Jack 概览。

Jack 库格式

Jack 有自己的 .jack 文件格式,其中包含相应库的预编译 dex 代码,可实现更快速的编译(dex 预处理)。

Jack 库文件内容。

图 2. Jack 库文件内容。

Jill

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

导入现有“jar.”库的工作流程。

图 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 编译因“Out of memory error”(内存不足错误)而失败 通过修改 $HOME/.jack 并将 SERVER_NB_COMPILE 改为较低的值来减少同时进行的 Jack 编译数量。
编译因“Cannot launch background server”(无法启动后台服务器)而失败 最可能的原因是您计算机上的 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 预处理。

包含 dex 预处理文件的 Jack 库。

图 4. 包含 dex 预处理文件的 Jack 库。

如果在编译过程中使用了缩减、混淆或重新打包功能,则 Jack 不会重复使用库的 dex 预处理文件。

增量编译

增量编译指的是,仅重新编译自上次编译后出现过更改的组件及其依赖项。当只有少数组件出现过更改时,进行增量编译可能比完整编译快得多。

增量编译默认处于停用状态(当缩减、混淆、重新打包或旧版 MultiDex 启用后,增量编译会自动被停用)。如需启用增量 build,请将以下行添加到您要进行增量构建的项目的 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 和旧版 MultiDex。由于 dex 文件的方法数上限为 65K,因此方法数超过 65K 的应用必须拆分成多个 dex 文件。有关详情,请参阅为方法数超过 64K 的应用启用 MultiDex