本地进行Android 编译时遇到一些问题,对Jack进行了一些了解,转载PCSXK兄的博客做记录。
Jack工具链
Jack是一个Android工具链,用于将Java源码编译成Android dex字节码。它替换了之前的Android工具链,即由javac
, ProGrard
, jarjar
和dx
多个工具组成的集合。
Jack工具链带来了以下优势:
-
完全开源
AOSP中可以找到;欢迎合作伙伴贡献。 -
加快编译速度
Jack对降低编译时间有具体的支持:pre-dexing,增量编译和Jack编译服务器。 -
处理压缩,混淆,重新打包和多dex
不再需要使用像ProGuard
之类单独的包。
图1. Java概览
.jack
库格式
Jack拥有它自己的文件格式.jack
,它包含了针对库的预编译dex代码,可以允许快速编译(pre-dex)。
图2. Jack库文件内容
Jill
Jill工具可以将现有的.jar
库文件转换成库格式,如下图所示。
图3. 导入现有的.jar
库的工作流
编译Android时使用Jack
为了使用Jack,你无需做任何不同的事情 - 只管使用标准的makefile命令来编译你的整体项目。Jack是M版本默认的Android编译工具链。
第一次使用Jack时,它会在你的电脑上启动一个本地Jack编译服务器:
- 这个服务器带来了内在的加速,因为它避免了在每一次编译时启动一个新的JRE JVM主机,加载Jack代码,初始化Jack和准备JIT。同时在小型的编译中它也提供了很好的编译时间(例如,增量模式)。
- 这个服务器也是控制并行Jack编译数量的一个短期解决方案,因此可以避免你的电脑过载(内存和磁盘问题),因为它限制了并行编译的数量。
在没有编译一段空闲时间之后,Jack服务器会关闭自己。它在本地接口使用了两个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的默认命令。默认情况下,该变量可以被环境变量覆盖。
[非翻译]目前的默认设置已经不是上面的值,所以这里的默认值仅供参考。
而且
$HOME/.jack
文件也找不到,只能找到$HOME/.jack-settings
文件。
Jack故障处理
如果编译时电脑无响应或以“Out of memory error”的错误编译失败
你可以通过编辑$HOME/.jack
文件将SERVER_NB_COMPILE
改为一个较低的值来降低Jack同时进行的编译数量来改进这个状况。
[非翻译]这个解决方案在实际当中并不能解决问题,具体解决方案参考我前边的文章 Android源代码编译笔记(支持5.x及以上版本)。
如果以“Cannot launch background server”的错误编译失败
最可能的原因是TCP端口已经被使用。尝试编辑$HOME/.jack
文件(SERVER_PORT_SERVICE
和SERVER_PORT_ADMIN
变量)来修复。
如果问题没有解决,情上报并且附上你的编译日志和Jack服务器日志(查看下方’查找Jack日志’了解从哪里找到服务器日志文件)。为了移除这个状况,编辑$HOME/.jack
文件将SERVER
改为false来禁用Jack编译服务器。不幸的是,这将会显著降低编译速度并且强制你启动make -j
(make
的选项-l
)。
如果编译被卡住没有任何进展
请上报给我们并且提供以下附加信息(尽可能):
- 卡住所在的命令行
- 命令行的输出
- 执行
jack-admin server-stat
的结果 $HOME/.jack
文件- 服务器状态为
dumped
时的服务器日志内容。为了获取这些
- 通过运行
jack-admin list-server
找出Jack后台服务器 - 发送
kill -3
命令到该服务器,将它的状态导出到日志文件 - 查看下面的
找出Jack日志
,来定位服务器日志文件
- 通过运行
- 执行
ls -lR $TMPDIR/jack-$USER
命令的结果 - 运行
ps j -U $USER
命令的结果
你可以通过杀掉Jack后台服务器(使用jack-admin kill-server
)来解除目前的情况,然后删除你的临时目录(/tmp
或者$TMPDIR
)下的jack-$USER
内的临时文件夹。
如果遇到其它错误
要上报bugs或者请求特性,请使用我们的公共问题跟踪器,在 http://b.android.com 上可用。使用Jack tool bug report或者Jack tool feature request模板。请将Jack的日志附到bug上报。
查找Jack日志
- 如果你使用dist target运行make命令,Jack的日志文件在
$ANDROID_BUILD_TOP/out/dist/logs/jack-server.log
- 否则你可以通过运行
jack-admin server-log
命令来查找
如果是可再生Jack失败,你可以通过设置一个变量得到更为详细的日志,如下所示:
<code class="hljs bash has-numbering">$ <span class="hljs-keyword">export</span> ANDROID_JACK_EXTRA_ARGS=<span class="hljs-string">"--verbose debug --sanity-checks on -D sched.runner=single-threaded"</span></code><ul style="" class="pre-numbering"><li>1</li><li>2</li></ul><ul style="" class="pre-numbering"><li>1</li><li>2</li></ul>
然后使用标准的makefile命令来编译整个项目或者你的项目并且附上它的标准输出和错误。
移除详细的编译日志可以使用:
<code class="hljs bash has-numbering">$ <span class="hljs-built_in">unset</span> ANDROID_JACK_EXTRA_ARGS</code><ul style="" class="pre-numbering"><li>1</li></ul><ul style="" class="pre-numbering"><li>1</li></ul>
Jack的局限性
使用Jack的特性
Jack支持Java 1.7并且继承了如下所述的额外特性。
Predexing
当生成Jack库文件时,作为pre-dex,库文件的.dex被生成和存储在.jack库文件的内部。当编译时,Jack从每个库中复用这些pre-dex。
所有的库都进行了pre-dexed。
图4. 带有pre-dex的Jack库
局限性
目前,如果在编译时使用了压缩/混淆/重新打包,那么Jack不能复用pre-dex的库。
增量编译
增量编译指的是从上次编译后,只有触摸过的部分和它们的依赖会被重新编译。当修改只发生在一小部分时,增量编译要比全部编译快很多。
局限性
当遗留的压缩,混淆,重新打包或者多dex启用时,增量编译会关闭。
启用增量编译
目前,增量编译没有默认开启。在你想要增量编译的项目的Android.mk文件中加入下面的代码:
<code class="hljs fix has-numbering"><span class="hljs-attribute">LOCAL_JACK_ENABLED :</span>=<span class="hljs-string"> incremental</span></code><ul style="" class="pre-numbering"><li>1</li></ul><ul style="" class="pre-numbering"><li>1</li></ul>
注意:使用Jack第一次编译你的项目时,如果某些依赖没有构建,使用
mma
命令构建它们,然后你就可以使用标准的构建命令。
压缩和混淆
Jack支持压缩和混淆并且可以使用混淆配置文件来开启压缩和混淆特性。这里是支持和忽略的选项:
支持的通用选项
通用选项包括以下:
- @
- -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
- -assumeonsideeffects
- -allowaccessmodification
- -mergeinterfacesaggressively
- -overloadaggressively
- -microedition
- -verbose
- -dontnote
- -dontwarn
- -ignorewarnings
- -printconfiguration
- -dump
其它选项将会产生错误。
重新打包
Jack使用jarjar的配置文件来进行重新打包。
Jack与”rule”规则类型兼容,但是不兼容”zap”或”keep”规则类型。如果你需要”zap”或”keep”规则类型,请发送一份特性请求并且附上在你的app里要如何使用这些特性的描述。
多dex支持
由于dex文件对于方法的上限是65K,所以超过65K方法的app必须要分成多个dex文件(有关多dex的更多信息参考 Building Apps with Over 65K Methods)。
Jack提供原生与遗留的多dex的支持。