Jack 是 Android 6.0-8.1 的默认 Android 构建工具链。
Jack 是一种 Android 工具链,用于将 Java 源代码编译成 Android dex 字节码。若要使用 Jack,您只需使用标准的 Makefile 命令来编译源代码树或您的项目即可,无需进行任何其他操作。Android 8.1 是使用 Jack 的最后一个版本。
Jack 简介
Jack 的工作原理如图 1 所示。
图 1. Jack 概览。
Jack 库格式
Jack 有自己的 .jack
文件格式,其中包含相应库的预编译 dex 代码,可实现更快速的编译(dex 预处理)。
图 2. Jack 库文件内容。
Jill
如下图所示,Jill 工具会将现有的 .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/.jack (SERVER_PORT_SERVICE 和 SERVER_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 预处理。
图 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