編譯 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

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 檔案以完整 bash 語法包含下列 Jack 伺服器變數的設定:

  • 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 編譯失敗 如要減少同時進行的 Jack 編譯作業數,請編輯 $HOME/.jack,並將 SERVER_NB_COMPILE 變更為較低的值。
編譯在無法啟動背景伺服器上失敗 最有可能的原因是電腦已使用 TCP 通訊埠。編輯 $HOME/.jack (SERVER_PORT_SERVICESERVER_PORT_ADMIN 變數) 即可變更連接埠。如要解除封鎖,請編輯 $HOME/.jack 並將 SERVER 變更為 false,藉此停用 Jack 編譯伺服器。很抱歉,這會大幅減緩您的編譯作業,並可能迫使您使用載入控制項啟動 make -j (make-l 選項)。
編譯停滯,沒有任何進度 如要解除封鎖,請使用 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 伺服器只能由電腦上的單一使用者使用。如要支援其他使用者,請為每位使用者選取不同的通訊埠編號,並據此調整 SERVER_NB_COMPILE。您也可以停用 Jack 伺服器,方法是在 $HOME/.jack 中設定 SERVER=false。由於目前的 vm-tests-tf 整合作業,CTS 編譯速度較慢。不支援位元碼操控工具 (例如 JaCoCo)。

使用 Jack

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

DEX 前置處理

產生 Jack 程式庫檔案時,系統會產生程式庫的 .dex,並將其儲存在 .jack 程式庫檔案中,做為前置 DEX。在編譯時,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 雖然與「rule」規則類型相容,但不支援「zap」或「keep」規則類型。

Multidex 支援

Jack 提供內建和舊版 multidex 支援。由於 dex 檔案的限制為 65,000 個方法,因此含有超過 65,000 個方法的應用程式必須分割為多個 dex 檔案。詳情請參閱「針對使用超過 64K 方法的應用程式啟用 Multidex」一文。