1. 程式人生 > >Android 新一代編譯 toolchain Jack & Jill 簡介

Android 新一代編譯 toolchain Jack & Jill 簡介

2016 年 3 月 10 日, Google 向外界釋出了 Android N 的預覽版,並宣佈了 Android N 的 Roadmap,Android N 的最終版原始碼將於今年 8 或 9 月份釋出到 AOSP 專案。

在眾多的 Android N 新特性中,有一項新工具鏈的出現與 Android 生態圈的所有開發者息息相關,即 Jack & Jill 編譯器的引入。

在依賴了 Sun/Oracle 的 Java 編譯器十年之後,Android 終於有了自己的 Java 編譯器。

本文試圖對市面上非常有限的資料進行總結,向大家介紹 Jack & Jill 的緣起,工作方式和原理。

Jack 是 Java Android Compiler Kit 的縮寫,它可以將 Java 程式碼直接編譯為 Dalvik 位元組碼,並負責 Minification, Obfuscation, Repackaging, Multidexing, Incremental compilation。它試圖取代 javac/dx/proguard/jarjar/multidex 庫等工具。

Jill 是 Jack Intermediate Library Linker 的縮寫,它負責 “Shielding JACK from Java byte code”;實際上輔助 Jack 對.class 做預處理,生成 .jack

 檔案

緣起

雖然 Google 是在宣佈 Android N 預覽版時隆重介紹了Jack & Jill。但是,早在 2014 年 Google 就對外宣佈了新編譯器 Jack 的存在 meet our new experimental toolchain, 它的開發啟動時間更是遠遠早於 2014 年。

下面是我總結的 Jack 的緣起

  • 一家名叫 FlexyCore 的小公司基於 GCC toolchain 開發了 Android 平臺上的 AOT 編譯器,被 Google 看中並於 2013 年被收購
  • FlexyCore team 基於 LLVM toolchain 開發了 ART,併成為 Android 5.0 之後的預設 Java Runtime
  • FlexyCore team 基於 Eclipse ecj 編譯器開始開發 Jack,基於 ASM4 開發 Jill。 他們早在 2014 年 2 月就開始提交 Jill 的程式碼了 Jill initial commit; 3 月份開始提交 Jack的程式碼 Jack initial commit
  • 自 Android build-tools 21.1 開始,裡面已經內建 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 認為 Jack 達到了一定的成熟度
  • 預計等 Android 7.0 正式釋出時,Jack 可能會成為官方推薦的編譯器

為什麼要拋棄 Javac/dx,開發 Jack 和 Jill

據個人推測主要有三個目的

  • 提高編譯速度
  • 應對 Oracle 的法律訴訟
  • 將編譯器掌控權拿在自己手中,不再受制於 Oracle,可以做一些 Android only 的優化

下面比較一下舊的 javac/dx/ProGuard/jarjar toolchain 和新的 Jack 編譯器的工作流程

舊編譯流程

簡單的說,將 Java 程式碼和依賴庫編譯為 dex 有兩個大的階段

javac (.java –> .class) –> dx (.class –> .dex)

下面是用流程圖表示的舊編譯過程

dx

  1. javac 將 java 程式碼編譯為 java bytecode, 以 .class 的形式存在; 以 jar 和 aar 形式存在的依賴庫,程式碼在裡面以一堆.class 的形式存在
  2. Proguard 工具讀取 Proguard 配置,對 .class 做 shrinking, obfuscation,輸出 Proguard mapping
  3. dx 將多個 .class 轉化為單一的 classes.dex ; 如果 dex 方法數超過 65k, 就生成 classes.dex, classes1.dex…classesN.dex

新編譯流程

新的編譯過程只有一個階段了,它完全拋棄了 javac, ProGuard, jarjar 等工具,一個工具搞定一切

Jack (.java –> .jack –> .dex)

下面是用流程圖表示的 Jill 預處理過程

jill

下面是用流程圖表示的 Jack 編譯過程

jack

  1. 各種依賴庫仍然以 jar/aar 的形式存在
  2. 輔助工具 Jill 將根據依賴庫中的 .class 生成 Jayce 格式的 IL,並呼叫 Jack 做 pre-dex 並生成 .jack,此過程只在編譯 app 時發生一次
  3. Jack 將 java 原始碼也編譯為 .jack,然後將多個 .jack 轉化為單一的 .dex; 如果 dex 方法數超過 65k, 就生成 classes.dex, classes1.dex…classesN.dex
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 的具體格式如下圖所示

.jack

可見裡面包含了 Jayce 格式的 IL ,pre-dex,原始 aar 中的資原始檔,以及 Jack 會用到的一些 meta 資訊

下圖簡單比較了 java 程式碼轉化的 .class, Jayce IL 和 dex 的內容異同

compare

簡單比較下三種 IL 的區別:

Sun/Oracle Hotspot VM 是基於棧式的,所以 .class 檔案的內容就是不斷地壓運算元到棧頂,從棧頂讀取運算元,比較或做運算,將結果再壓回棧頂

Dalvik VM 是基於暫存器的,所以 .dex 的內容就是不斷地 move 運算元到暫存器,比較或做運算,將結果寫回暫存器或記憶體地址

Jayce 則是 Jack&Jill 專有的 IL, 目前沒有查閱到更多的官方資料。只能參閱 Jill 原始碼中 com.android.jill.backend.jayce 包的程式碼了,比如其中的 Token 類就定義了 Jayce 的 Token 定義。

個人推測 Jayce 存在的意義是:

  • 為了在整合多個 jack 檔案,生成單一的 dex 時,方便 Jack 做一些全域性性的後端編譯優化。
  • 從 Android 生態圈中完全去除 Oracle 的 Java Bytecode 格式

使用Jack編譯器的優勢

  • 對依賴庫做 pre dex,且成果會被儲存到 build/intermediates/jill/debug 目錄。

之後的編譯過程中,只要依賴庫的數目和版本不變,之前的 pre dex 成果會被複用;Jack 只需要編譯變化的原始碼,然後對多個 dex 進行 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 的預設編譯器
  • 向後相容到 Android 2.3

採用 Jack 對打包流程的影響

  1. 不再需要獨立的 ProGuard。Jack 支援讀取舊的 ProGuard 配置,完成 shrinking, obfuscation 的工作
  2. 不再需要獨立的 jarjar。Jack 支援讀取舊的 jarjar 配置,完成 repackaging 的工作
  3. 沒有 .class 檔案了,直接操縱或讀取 Java 位元組碼的各種工具如 JaCoCo/Lint/Mokito/Retrolambda 沒有了用武之地。但是仍然可以在 Android Library 上使用這些工具,編譯為 aar/jar 後作為 Jill 的輸入
  4. annotation processors 如 Dagger, ButterKife 仍可以使用
  5. Scala/Kotlin 等第三方 JVM 語言編寫的內容必須先被 Jill 處理,再作為 Jack 的輸入

Jack 當前的侷限(截止到2016/03/15)

  1. 暫時還不支援 Android Studio 2.0 的 Instant Run 特性
  2. 暫時還不支援 data binding

65k 方法數目問題

為什麼會有 65k 問題?

當你的 app 足夠複雜之後,在打包時常常會遇到這種錯誤提示

Unable to execute dex: method ID not in [0, 0xffff]: 65536

為什麼方法數目不能超過 65k 呢?有人說是 dexopt 的問題,有人說是 dex 格式的限制,下面我們看看這個 log 到底是哪裡吐出來的,然後分析下具體原因。

  • dex 格式的限制?

首先我們看一下 dex 的結構定義

//Direct-mapped "header_item" struct.
struct DexHeader {
	...
  u4  methodIdsSize;
	...
};

//These match the definitions in the VM specification.
typedef uint32_t            u4;

可見 dex 檔案結構是用 32 位來儲存 method id 的,最大支援 2 的 32 次方,因此 65k 的原因不在於此。

  • dexopt 的原因?

dexopt 是 app 已經打包成功,安裝到手機之後才會發生的過程。但是 65k 問題是在打包時發生的,所以問題原因也不在此

一般提到的 dexopt 錯誤,其實是 Android 2.3 及其以下在 dexopt 執行時只分配 5M 記憶體,導致方法數目過多(數量不一定到 65k)時在 odex 過程中崩潰,官方稱之為 Dalvik linearAlloc bug(Issue 22586) 。

另:這個 linearAlloc 的限制不僅存在於 dexopt 裡,還在 dalvik rumtime 中存在……

  • 錯誤 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 [email protected] or [email protected]
*/

public static final int MAX_MEMBER_IDX = 0xFFFF;

通過查閱 dalvik-bytecode 可知,@CCCC 的範圍必須在 0~65535 之間。

所以歸根結底,65k 問題是因為 dalvik bytecode 中的指令格式使用了 16 位來放 @CCCC 導致的;所以,不僅 Method 數目不能超過 65k, Field 和 Class 數目也不能超過 65k。

為什麼 jack 沒有 65k 問題

前文已經很清楚地解釋了 65k 問題的由來,可見只要 dalvik bytecode 指令格式不升級,65k 問題是逃不掉的。

Jack 官網對 65k 問題是這麼說的:

Multidex support

Since 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 執行時也和以前一樣

  1. 若是 dalvik 虛擬機器,它只支援讀取一個 classes.dex。而 multidex 解決方案會讀取多個 .dex,幫我們做 dex 數組合並
  2. 若是 art 虛擬機器,它會掃描 classes.dex, classes1.dex…classesN.dex,呼叫 dex2oat 轉化為單一的 oat

Jack 是怎麼支援 Java 8 的?

以 lambda 表示式為例

Interface lambda = i -> i + 1;

會被轉化為 anonymous classes

Interface lambda = new Interface() {
  public int m(int i) {
    return i + 1;
  }
};

Jack當前支援的 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"
    }
    

效能比較

相關推薦

Android 一代編譯 toolchain Jack & Jill 簡介

2016 年 3 月 10 日, Google 向外界釋出了 Android N 的預覽版,並宣佈了 Android N 的 Roadmap,Android N 的最終版原始碼將於今年 8 或 9 月份釋出到 AOSP 專案。 在眾多的 Android N 新特性中

【騰訊Bugly乾貨分享】Android 一代多渠道打包神器

關於作者: 李濤,騰訊Android工程師,14年加入騰訊SNG增值產品部,期間主要負責手Q動漫、企鵝電競等專案的功能開發和技術優化。業務時間喜歡折騰新技術,寫一些技術文章,個人技術部落格:www.ltlovezh.com 。 ApkChanne

Android 一代多渠道打包神器

ApkChannelPackage是一種快速多渠道打包工具,同時支援基於V1和V2簽名進行渠道打包。外掛本身會自動檢測Apk使用的簽名方法,並選擇合適的多渠道打包方式,對使用者來說完全透明。 概述 眾所周知,因為國內Android應用分發市場的現狀,我們在釋出APP

一代 Linux 檔案系統 btrfs 簡介

btrfs 的特性和使用 Btrfs 被稱為是下一代 Linux 檔案系統。近年來 ext2/3 遇到越來越多的擴充套件性問題,在期待 ext4 的同時,人們發現了 btrfs,據說它採用了很多先進的檔案系統設計,不僅解決了 ext2/3 的擴充套件性問題

知識篇:一代的數據處理平臺Hadoop簡介

Hadoop在雲計算和大數據大行其道的今天,Hadoop及其相關技術起到了非常重要的作用,是這個時代不容忽視的一個技術平臺。事實上,由於其開源、低成本和和前所未有的擴展性,Hadoop正成為新一代的數據處理平臺。Hadoop是基於Java語言構建的一套分布式數據處理框架,從其歷史發展角度我們就可以看出,Had

android sutdio專案編譯失敗

在repositories目錄下加入maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'} buildscript { repositories { google() m

解讀一代崗位要求(一) ----------------Android開發工程師

android開發工程師 / 15k-30k 國際化團隊、東南亞獨角獸 職位描述: 工作職責: 1、負責產品迭代改進及移動新產品的開發;  2、參與 APP 效能、體驗優化及質量監控評估體系建設;  3、參與客戶端基礎元件及架構設計, 推進研發效率。&

如何用NDK建立一個標準的android環境交叉編譯工具鏈(Cross Toolchain)

轉自:http://blog.sina.com.cn/s/blog_4a0a39c30101q1u4.htmlhttp://www.linuxidc.com/Linux/2014-11/109905.htmandroid原始碼在 prebuilts 目錄已經自帶有交叉編譯工具

如何用NDK建立一個標準的android環境交叉編譯工具鏈(Cross Toolchain)(轉)

android原始碼在 prebuilts 目錄已經自帶有交叉編譯工具鏈,但這個還不是一個標準的可直接編譯程式碼的編譯器,還需要依賴android ndk 的標頭檔案及庫檔案,才能進行編譯和連結,且版本也比較保守(4.0原始碼自帶的是gcc 4.4.3,

的驅動原始檔增進android核心進行編譯

1,同目錄下的makefile,如 # # Makefile for industrial I/O Magnetometer sensors # obj-$(CONFIG_SENSORS_AK8975)    += ak8975.o obj-$(CONFIG_SENSORS_HMC5843)  

fetch簡介: 一代Ajax API

AJAX半遮半掩的底層API是飽受詬病的一件事情. XMLHttpRequest 並不是專為Ajax而設計的. 雖然各種框架對 XHR 的封裝已經足夠好用, 但我們可以做得更好。更好用的API是 fetch 。下面簡單介紹 window.fetch 方法,

Android增資原始檔編譯,R檔案未重新生成解決辦法

在修改Launcher2專案中,新增資原始檔後出現莫名奇怪的錯誤 型別轉換錯誤, 查看了相關檔案 mUnistallDrawable是全域性變數,也是TransitionDrawable物件,查看了unistall_target_selector.

一代開源Android渠道包生成工具Walle

在Android 7.0(Nougat)推出了新的應用簽名方案APK Signature Scheme v2後,之前快速生成渠道包的方式(美團Android自動化之旅—生成渠道包)已經行不通了,在此應用簽名方案下如何快速生成渠道包呢?本文會對新的應用簽名方案APK Signa

Android入門級編譯錯誤匯總

art can 手動 already 提示 文件夾 som 原因 兩個 1 描寫敘述: 項目常常須要引用別人的libraryproject,在選項中add進來後,點擊應用或者確定。關閉頁面。 回到代碼中卻發現無法鏈接,又一次打開properties查看,發現導入的p

一代Xamarin

c++類庫 現在 下載 演示 復用 多少 ++ 類庫 神奇 新一代Xamarin竟然可以將.NET代碼原生編譯成:Jar包供Java原生調用、swift類庫、obj-c類庫、C++類庫 供目標平臺傳統代碼直接調用之前和很多朋友聊到Xamarin覺得確實不錯,原生性能,研發

android源碼編譯——從此走上Liunx的不歸路(二)

彈出 oid log 按鈕 鍵盤 點擊 使用 andro android Ubuntu安裝:   1.啟動虛擬機進入到如下界面:         2.下拉找到“中文(簡體)”選項,然後選擇“安裝Ubuntu”:         3.點擊繼續:         4.選擇清除整

android源碼編譯——從此走上Liunx的不歸路(三)

article down 安裝git https ani 同步 版本 bsp rep 下載android源碼: 1.安裝git和curl: sudo apt-get install git-core sudo apt-get install git-core curl 2

Android Freeline加速編譯App方案 使用和總結

lis adl ebo pos tps 螞蟻 優化 void list Freeline簡單介紹 在Android Studio還沒推出Instant Run功能之前,每次改動Android project項目時都要將整個項目又一次編譯一次,然後再將資

android源碼編譯時拷貝替換指定文件

android cau value 定制 部分 get android源碼 oal first 由於要做版本定制,某些版本的資源文件等(例如style.xml)需要不同的配置,但是android的編譯開關無法在xml裏使用,於是想到了編譯時根據不同的編譯開關編譯不同的文件,

安全牛:“一代SOC研究報告”之市場指南及技術指南點評

安全管理平臺 soc 安管平臺 isoc 安全牛 在2017年8月9日,國內知名的企業級信息安全市場的專業新媒體——安全牛——發布了“新一代SOC研究報告”之市場指南及技術指南。在指南正式發布之前,7月份的阿裏安全峰會(以後改成“網絡安全生態峰會”)上,安全牛背後的谷安天下負責人李華在論壇