目前Android studio 2.4預(yù)覽版不再需要使用jack 工具鏈了
這件事要從一次報錯開始
錯誤的原因是要使用新的 Java 8 語言功能桦沉,還需使用新的 Jack 工具鏈每瞒。
那么在Android Studio中使用Java 8應(yīng)該如何配置呢?
看這里——使用 Java 8 語言功能
android {
...
android {
...
defaultConfig {
...
jackOptions {
enabled true
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
如果僅僅是想使用Lambda 纯露,可以使用第三方開源的gradle-retrolambda
Retrolambda 是一個 Java 類庫剿骨,在 Java 5、6埠褪、7 上實現(xiàn) Java 8 的 Lambda 表達(dá)式的功能浓利。你可以通過 Retrolambda 讓使用 Lambda 表達(dá)式的 Java 8 代碼運行在 Java 7 或更低版本上。也就是說Retrolambda是可以將Java 8的源文件編譯成低版本的.class文件钞速。
注意:
-
targetCompatibility 1.8
則必須使用 Jack 工具鏈 - 但是使用 Jack 工具鏈贷掖,targetCompatibility 不做要求,只需要Build Tools 24.0.0 or later——SDK Build Tools Release Notes渴语,并且
jackOptions {
enabled true
}
This is Jack .#
Jack is a new Android toolchain that compiles Java source into Android dex bytecode. It replaces the previous Android toolchain, which consists of multiple tools, such as javac, ProGuard, jarjar, and dx.
Jack是一個新的 Android 工具鏈苹威,它將 Java 源語言編譯成 Android 可讀取的 Dalvik 可執(zhí)行文件字節(jié)碼,且有其自己的 .jack 庫格式驾凶,在一個工具中提供了大多數(shù)工具鏈功能:重新打包牙甫、壓縮、模糊化以及 Dalvik 可執(zhí)行文件分包调违。
以下是構(gòu)建 Android Dalvik 可執(zhí)行文件可用的兩種工具鏈的對比:
- 舊版 javac 工具鏈:
javac (.java → .class) → dx (.class → .dex) - 新版 Jack 工具鏈:Jack (.java → .jack → .dex)
參考文章
class 文件與dex文件區(qū)別 (dvm與jvm區(qū)別)及Android DVM介紹
Android 開發(fā)簡介-系統(tǒng)架構(gòu)
Android 新一代編譯 toolchain Jack & Jill 簡介
使用Jack編譯
android 6.0 jack 編譯詳解
Android的Jack編譯器試用
以下內(nèi)容轉(zhuǎn)載自Android 新一代編譯 toolchain Jack & Jill 簡介窟哺,作者: 凱馮 發(fā)表于: 2016-05-05#
因為著實文章很全面,怕萬一哪天鏈接掛了翰萨,所以轉(zhuǎn)載過來脏答,作為收藏
2016 年 3 月 10 日, Google 向外界發(fā)布了 Android N 的預(yù)覽版亩鬼,并宣布了 Android N 的 Roadmap殖告,Android N 的最終版源代碼將于今年 8 或 9 月份釋出到 AOSP 項目。
在眾多的 Android N 新特性中雳锋,有一項新工具鏈的出現(xiàn)與 Android 生態(tài)圈的所有開發(fā)者息息相關(guān)黄绩,即 Jack & Jill 編譯器的引入。
在依賴了 Sun/Oracle 的 Java 編譯器十年之后玷过,Android 終于有了自己的 Java 編譯器爽丹。
本文試圖對市面上非常有限的資料進(jìn)行總結(jié),向大家介紹 Jack & Jill 的緣起辛蚊,工作方式和原理粤蝎。
Jack 是 Java Android Compiler Kit 的縮寫,它可以將 Java 代碼直接編譯為 Dalvik 字節(jié)碼袋马,并負(fù)責(zé) Minification, Obfuscation, Repackaging, Multidexing, Incremental compilation初澎。它試圖取代 javac/dx/proguard/jarjar/multidex 庫等工具。
git 源代碼地址是 https://android.googlesource.com/toolchain/jack虑凛。
Jill 是 Jack Intermediate Library Linker 的縮寫碑宴,它負(fù)責(zé) “Shielding JACK from Java byte code”;實際上輔助 Jack 對.class 做預(yù)處理桑谍,生成 .jack
文件
git 源代碼地址是 https://android.googlesource.com/toolchain/jill延柠。
緣起#
雖然 Google 是在宣布 Android N 預(yù)覽版時隆重介紹了Jack & Jill。但是锣披,早在 2014 年 Google 就對外宣布了新編譯器 Jack 的存在 meet our new experimental toolchain, 它的開發(fā)啟動時間更是遠(yuǎn)遠(yuǎn)早于 2014 年贞间。
下面是我總結(jié)的 Jack 的緣起
- 一家名叫 FlexyCore 的小公司基于 GCC toolchain 開發(fā)了 Android 平臺上的 AOT 編譯器,被 Google 看中并于 2013 年被收購
- FlexyCore team 基于 LLVM toolchain 開發(fā)了 ART雹仿,并成為 Android 5.0 之后的缺省 Java Runtime
- FlexyCore team 基于 Eclipse ecj 編譯器開始開發(fā) Jack榜跌,基于 ASM4 開發(fā) Jill。 他們早在 2014 年 2 月就開始提交 Jill 的代碼了 Jill initial commit盅粪; 3 月份開始提交 Jack的代碼 Jack initial commit
- 自 Android build-tools 21.1 開始钓葫,里面已經(jīng)內(nèi)置 jack.jar 和 jill.jar
- Android Gradle plugin 自 0.14 開始支持 Jack & Jill initial commit
- 自 Android 6.0 開始,Jack & Jill 成為 AOSP 的官方編譯器, 也就是說所有的 Android 6.0 ROM 都是 Jack 編譯出來的 link票顾,也代表 Google 認(rèn)為 Jack 達(dá)到了一定的成熟度
- 預(yù)計等 Android 7.0 正式發(fā)布時础浮,Jack 可能會成為官方推薦的編譯器
為什么要拋棄 Javac/dx,開發(fā) Jack 和 Jill#
據(jù)個人推測主要有三個目的
- 提高編譯速度
- 應(yīng)對 Oracle 的法律訴訟
- 將編譯器掌控權(quán)拿在自己手中,不再受制于 Oracle奠骄,可以做一些 Android only 的優(yōu)化
下面比較一下舊的 javac/dx/ProGuard/jarjar toolchain 和新的 Jack 編譯器的工作流程
舊編譯流程#
簡單的說豆同,將 Java 代碼和依賴庫編譯為 dex 有兩個大的階段
javac (.java –> .class) –> dx (.class –> .dex)
下面是用流程圖表示的舊編譯過程
- javac 將 java 代碼編譯為 java bytecode, 以 .class 的形式存在; 以 jar 和 aar 形式存在的依賴庫,代碼在里面以一堆.class 的形式存在
- Proguard 工具讀取 Proguard 配置含鳞,對 .class 做 shrinking, obfuscation影锈,輸出 Proguard mapping
- dx 將多個 .class 轉(zhuǎn)化為單一的 classes.dex ; 如果 dex 方法數(shù)超過 65k, 就生成 classes.dex, classes1.dex…classesN.dex
新編譯流程#
新的編譯過程只有一個階段了,它完全拋棄了 javac, ProGuard, jarjar 等工具,一個工具搞定一切
Jack (.java –> .jack –> .dex)
下面是用流程圖表示的 Jill 預(yù)處理過程
下面是用流程圖表示的 Jack 編譯過程
- 各種依賴庫仍然以 jar/aar 的形式存在
- 輔助工具 Jill 將根據(jù)依賴庫中的 .class 生成 Jayce 格式的 IL鸭廷,并調(diào)用 Jack 做 pre-dex 并生成 .jack炬灭,此過程只在編譯 app 時發(fā)生一次
- Jack 將 java 源代碼也編譯為 .jack久锥,然后將多個 .jack 轉(zhuǎn)化為單一的 .dex; 如果 dex 方法數(shù)超過 65k, 就生成 classes.dex, classes1.dex…classesN.dex
pre-dex 的詳細(xì)解釋可以參閱此鏈接 new-build-system
Improving Build Server performance.The Gradle based build system has a strong focus on incremental builds. One way it is doing this in doing pre-dexing on the dependencies of each modules, so that each gets turned into its own dex file (ie converting its Java bytecode into Android bytecode). This allows the dex task to do less work and to only re-dex what changed and merge all the dex files.
Jack中間文件#
.Jack 的具體格式如下圖所示
可見里面包含了 Jayce 格式的 IL 吆倦,pre-dex灵临,原始 aar 中的資源文件,以及 Jack 會用到的一些 meta 信息
下圖簡單比較了 java 代碼轉(zhuǎn)化的 .class, Jayce IL 和 dex 的內(nèi)容異同
簡單比較下三種 IL 的區(qū)別:
Sun/Oracle Hotspot VM 是基于棧式的讼载,所以 .class 文件的內(nèi)容就是不斷地壓操作數(shù)到棧頂轿秧,從棧頂讀取操作數(shù),比較或做運算咨堤,將結(jié)果再壓回棧頂
Dalvik VM 是基于寄存器的菇篡,所以 .dex 的內(nèi)容就是不斷地 move 操作數(shù)到寄存器,比較或做運算一喘,將結(jié)果寫回寄存器或內(nèi)存地址
Jayce 則是 Jack&Jill 專有的 IL, 目前沒有查閱到更多的官方資料驱还。只能參閱 Jill 源代碼中 com.android.jill.backend.jayce 包的代碼了,比如其中的 Token 類就定義了 Jayce 的 Token 定義津滞。
個人推測 Jayce 存在的意義是:
- 為了在整合多個 jack 文件铝侵,生成單一的 dex 時,方便 Jack 做一些全局性的后端編譯優(yōu)化触徐。
- 從 Android 生態(tài)圈中完全去除 Oracle 的 Java Bytecode 格式
使用Jack編譯器的優(yōu)勢#
- 對依賴庫做 pre dex咪鲜,且成果會被保存到 build/intermediates/jill/debug 目錄。
之后的編譯過程中撞鹉,只要依賴庫的數(shù)目和版本不變疟丙,之前的 pre dex 成果會被復(fù)用;Jack 只需要編譯變化的源代碼鸟雏,然后對多個 dex 進(jìn)行 merge 即可享郊,能夠加速整個編譯過程。 - 編譯時會啟動一個 Jack compilation server孝鹊,并開啟并行編譯
Jack 文檔是這么介紹的
This server brings an intrinsic speedup, because it avoids launching a new host JRE JVM, loading Jack code, initializing Jack and warming up the JIT at each compilation. It also provides very good compilation times during small compilations (e.g. in incremental mode).The server is also a short-term solution to control the number of parallel Jack compilations, and so to avoid overloading your computer (memory or disk issue), because it limits the number of parallel compilations.
- 支持 Java 8 的一部分特性
- Jack 由 Google 完全掌控炊琉,未來可能成為 Android sdk 的默認(rèn)編譯器
- 向后兼容到 Android 2.3
采用 Jack 對打包流程的影響#
- 不再需要獨立的 ProGuard。Jack 支持讀取舊的 ProGuard 配置又活,完成 shrinking, obfuscation 的工作
- 不再需要獨立的 jarjar苔咪。Jack 支持讀取舊的 jarjar 配置,完成repackaging 的工作
- 沒有 .class 文件了柳骄,直接操縱或讀取 Java 字節(jié)碼的各種工具如JaCoCo/Lint/Mokito/Retrolambda 沒有了用武之地团赏。但是仍然可以在 Android Library 上使用這些工具,編譯為 aar/jar 后作為 Jill 的輸入
- annotation processors 如 Dagger, ButterKife 仍可以使用
- Scala/Kotlin 等第三方 JVM 語言編寫的內(nèi)容必須先被 Jill 處理耐薯,再作為 Jack 的輸入
Jack-當(dāng)前的局限-截止到2016-03-15#
- 暫時還不支持 Android Studio 2.0 的 Instant Run 特性
- 暫時還不支持 data binding
65k 方法數(shù)目問題#
為什么會有 65k 問題
當(dāng)你的 app 足夠復(fù)雜之后舔清,在打包時常常會遇到這種錯誤提示
Unable to execute dex: method ID not in [0, 0xffff]: 65536
為什么方法數(shù)目不能超過 65k 呢丝里?有人說是 dexopt 的問題,有人說是 dex 格式的限制体谒,下面我們看看這個 log 到底是哪里吐出來的杯聚,然后分析下具體原因。
- dex 格式的限制营密?
首先我們看一下 dex 的結(jié)構(gòu)定義
//Direct-mapped "header_item" struct.
struct DexHeader {
...
u4 methodIdsSize;
...
};
//These match the definitions in the VM specification.
typedef uint32_t u4;
可見 dex 文件結(jié)構(gòu)是用 32 位來存儲 method id 的械媒,最大支持 2 的 32 次方目锭,因此 65k 的原因不在于此评汰。
- dexopt 的原因?
dexopt 是 app 已經(jīng)打包成功,安裝到手機之后才會發(fā)生的過程痢虹。但是 65k 問題是在打包時發(fā)生的被去,所以問題原因也不在此
一般提到的 dexopt 錯誤,其實是 Android 2.3 及其以下在 dexopt 執(zhí)行時只分配 5M 內(nèi)存奖唯,導(dǎo)致方法數(shù)目過多(數(shù)量不一定到 65k)時在 odex 過程中崩潰惨缆,官方稱之為 Dalvik linearAlloc bug(Issue 22586) 。
另:這個 linearAlloc 的限制不僅存在于 dexopt 里丰捷,還在 dalvik rumtime 中存在……
以下鏈接詳細(xì)解釋了此問題:https://github.com/simpleton/dalvik_patch
- 錯誤 log 是哪里吐出來的?
//MemberIdsSection.java
if (items().size() > DexFormat.MAX_MEMBER_IDX + 1) {
throw new DexIndexOverflowException(getTooManyMembersMessage());
}
/*
Maximum addressable field or method index.
The largest addressable member is 0xffff, in the "instruction formats" spec as field@CCCC or meth@CCCC.
*/
public static final int MAX_MEMBER_IDX = 0xFFFF;
通過查閱 dalvik-bytecode 可知坯墨,@CCCC 的范圍必須在 0~65535 之間。
所以歸根結(jié)底病往,65k 問題是因為 dalvik bytecode 中的指令格式使用了 16 位來放 @CCCC 導(dǎo)致的;所以停巷,不僅 Method 數(shù)目不能超過 65k, Field 和 Class 數(shù)目也不能超過 65k耍攘。
為什么 jack 沒有 65k 問題#
前文已經(jīng)很清楚地解釋了 65k 問題的由來,可見只要 dalvik bytecode 指令格式不升級畔勤,65k 問題是逃不掉的蕾各。
Jack 官網(wǎng)對 65k 問題是這么說的:
Multidex supportSince dex files are limited to 65K methods, apps with over 65K methods must be split into multiple dex files. (See ‘Building Apps with Over 65K Methods’ for more information about multidex.)Jack offers native and legacy multidex support.
所以,Jack 和舊工具鏈對 multidex 的支持方式是相同的
被 Jack 編譯出來的 app 執(zhí)行時也和以前一樣
- 若是 dalvik 虛擬機庆揪,它只支持讀取一個 classes.dex式曲。而 multidex 解決方案會讀取多個 .dex,幫我們做 dex 數(shù)組合并
- 若是 art 虛擬機缸榛,它會掃描 classes.dex, classes1.dex…classesN.dex吝羞,調(diào)用 dex2oat 轉(zhuǎn)化為單一的 oat
Jack-是怎么支持 Java 8 的?#
以 lambda 表達(dá)式為例
Interface lambda = i -> i + 1;
會被轉(zhuǎn)化為 anonymous classes
Interface lambda = new Interface() {
public int m(int i) {
return i + 1;
}
};
Jack當(dāng)前支持的 Java 8 特性可參見 j8-jack仔掸。
如何在 Gradle 腳本中使用 Jack 編譯器編譯 app#
想使用 Jack 和 Jill 需要指定你的 Build Tools version 是 21.1.0+, Gradle plugin version 是1.0.0+脆贵。
以下的配置是我個人測試通過的配置
- 使用 Android Gradle 插件 2.1.0-alpha2
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0-alpha2'
}
- 使用以下版本的 sdk 和 build-tool
compileSdkVersion 'android-N'
buildToolsVersion '24.0.0 rc1'
- 在 defaultConfig 中指定用 Jack
defaultConfig {
jackOptions {
enabled true
}
}
- 使用 gradle 2.10 以上
distributionUrl=http\://mirrors.taobao.net/mirror/gradle/gradle-2.10-bin.zip
使用 Android Studio 2.1 (preview) 或者命令行編譯
可能需要提升 javaMaxHeapSize
dexOptions{
javaMaxHeapSize "2g"
}
性能比較#
經(jīng)過測試,當(dāng)前版本(2016/03/15)的 Jack 編譯器比起 Javac+dx 在編譯時間起暮,編譯出的 apk 體積卖氨,編譯出的 apk 的性能上暫時并沒有優(yōu)勢会烙。
但是,可以期待 Google 將在 Jack 編譯器上做大量的智力投資筒捺,Jack 的未來是光明的柏腻。
下圖是 guardsquare 公司對 Javac+dx 和 Jack 做的對比測試
對于不 proguard 的 clean build,javac/dx 耗時 56s系吭, jack 耗時 1 m 48 s五嫂;之所以 jack 這么慢是因為它要做大量的 pre-dex。
對于不 proguard 的 clean build肯尺,javac/dx 和 jack 編譯出來的 app 性能相差無幾沃缘。
對于共用 proguard 配置文件情況,javac/dx 和jack 編譯出來的 app 體積也差不多则吟。
我個人測試的編譯速度 / apk 體積等對比也大致如此槐臀,在此不再贅述.
結(jié)語#
雖然 Jack 編譯器的現(xiàn)狀并不出彩,但是它終究有一天會成為 Android app 的官方推薦編譯器氓仲。
期待 Google Android team 加倍努力水慨,讓這一天早日到來。
參考文獻(xiàn)#
- https://www.guardsquare.com/blog/the_upcoming_jack_and_jill_compilers_in_android
- http://source.android.com/devices/tech/dalvik/dex-format.html
- http://tools.android.com/tech-docs/jackandjill
- https://developer.android.com/intl/zh-cn/tools/building/multidex.html
- https://www.guardsquare.com/blog/DroidconLondon2015