使用 Jack 編譯

Jack 是 Android 6.0 至 8.1 的預設 Android 建構工具鍊,用於將 Java 來源編譯為 Android dex 位元碼,如圖 1 所示:

Jack 總覽。

圖 1. Jack 總覽。

.jack 檔案格式包含程式庫的預先編譯 dex 程式碼,可加快編譯速度 (dex 前置處理)。

Jack 程式庫檔案內容。

圖 2. Jack 程式庫檔案內容。

Jill 工具

如圖 3 所示,Jill 工具會將現有的 .jar 程式庫轉換為新的 .jack 程式庫格式:

匯入現有 `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 記錄

如果您使用 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。您也可以在 $HOME/.jack 中設定 SERVER=false,停用 Jack 伺服器。由於目前的 vm-tests-tf 整合,CTS 編譯速度緩慢。不支援位元碼操控工具 (例如 JaCoCo)。

使用 Jack

Jack 支援 Java 程式設計語言 1.7,並整合本節所述的其他功能。使用標準 makefile 指令編譯樹狀結構或專案。

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 與 rule 規則類型相容,但與 zapkeep 規則類型不相容。

支援 Multidex

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