編譯 Jack (Android 開放原始碼計畫 6.0 - 8.1)

Jack 是 Android 6.0 至 8.1 的預設 Android 建構工具鍊

Jack 是 Android 工具鍊,可將 Java 來源編譯為 Android dex 位元碼。使用 Jack 時,您不必採取任何其他行動,只要使用標準的 makefile 指令編譯樹狀結構或專案即可。Android 8.1 是最後一個使用 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 伺服器就會自行關閉。 這個介面會使用本機介面上的兩個 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 編譯問題

問題 動作
編譯期間電腦沒有回應,或發生 Out of memory 錯誤,導致 Jack 編譯失敗 編輯 $HOME/.jack 並將 SERVER_NB_COMPILE 變更為較低的值,即可減少同步 Jack 編譯的數量。
無法啟動背景伺服器,導致編譯失敗 最可能的原因是電腦上已使用 TCP 連接埠。如要變更通訊埠,請編輯 $HOME/.jack (SERVER_PORT_SERVICESERVER_PORT_ADMIN 變數)。如要解除封鎖,請編輯 $HOME/.jack 並將 SERVER 變更為 false,藉此停用 Jack 編譯伺服器。很抱歉,這會大幅減緩編譯速度,並可能導致您必須使用載入控制項 (make 的選項 -l) 啟動 make -j
編譯作業停滯,沒有任何進展 如要解除封鎖,請使用 jack-admin kill-server 終止 Jack 背景伺服器,然後移除臨時目錄 (/tmp$TMPDIR) jack-$USER 中的臨時目錄。

尋找 Jack 記錄

如果您使用發布目標執行 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 伺服器。如要支援其他使用者,請為每位使用者選取不同的通訊埠號碼,並相應調整 SERVER_NB_COMPILE。您也可以在 $HOME/.jack 中設定 SERVER=false,停用 Jack 伺服器。由於目前的 vm-tests-tf 整合,CTS 編譯速度緩慢。 不支援位元碼操控工具 (例如 JaCoCo)。

使用 Jack

Jack 支援 Java 程式設計語言 1.7,並整合下列其他功能。

Pre-dex

產生 Jack 程式庫檔案時,系統會產生程式庫的 .dex,並以 DEX 前置處理的形式儲存在 .jack 程式庫檔案中。編譯時,Jack 會重複使用每個程式庫的 DEX 前置處理。所有程式庫都已預先 dex 化。

使用預先 Dex 處理的 Jack 程式庫。

圖 4. 使用預先 Dex 處理的 Jack 程式庫。

如果編譯時使用縮減、模糊處理或重新封裝,Jack 就不會重複使用程式庫的預先 Dex 檔案。

增量編譯

增量編譯是指只重新編譯上次編譯後變更的元件 (及其依附元件)。如果變更僅限於一組元件,增量編譯的速度會比完整編譯快上許多。

增量編譯功能預設為停用 (啟用縮減、混淆、重新封裝或多重 DEX 舊版功能時,系統會自動停用這項功能)。如要啟用累加建構作業,請在要累加建構的專案 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 支援「規則」規則類型,但不支援「Zap」或「保留」規則類型。

支援 Multidex

Jack 提供內建和舊版 multidex 支援。由於 dex 檔案最多只能有 65,000 個方法,因此如果應用程式的方法超過 65,000 個,就必須分割成多個 dex 檔案。詳情請參閱「針對使用超過 64K 方法的應用程式啟用 Multidex