提高android gradle構建速度的努力
目前看, gradle命令列選項, 使用jack(想繞過class->dex這步), 以及取消lint, multi-dex, proguard(這三樣確實耗時, 但不是最關鍵的), 都不能顯著提供android-gradle-plugin的構建速度(assembleDebug為例), 耗時就是出在了dexDebug(50多秒), packageDebug(10多秒)這兩個任務上(android-gradle-plugin:1.3.0為例), 既然是dex, 我嘗試了dexOptions, 發現真的可以.
新增以下程式碼到build.gradle能提高構建速度:
dexOptions {
incremental true //如果開啟multi-dex, 此句無效
preDexLibraries true
javaMaxHeapSize "4g" //最主要是這句, 不過改成8g沒有更顯著的提升
// jumboMode true
// threadCount 8 //gradle輸就輸在了並行上, 都是序列, 增加執行緒數沒鳥用
// maxProcessCount 8 //com.android.tools.build:gradle:2.0.0
}
既然堆大小有這樣顯著的效果, 那我想能不能提高gradle環境的記憶體使用大小, 於是在gradle.properties中做了如下配置:
org.gradle .parallel=true
org.gradle.daemon=true
org.gradle.configureondemand=true
# default workers count equals processor count
# org.gradle.workers.max=8
org.gradle.jvmargs=-Xms4096m -Xmx8192m -Dfile.encoding=UTF-8
但是前幾個在處理configuration依賴速度有所提高, 不過我認為最關鍵的那個jvmargs, 不知道是不是預設值就已經很高了? 反正加上以後沒有看到有多大的提升;
(以上構建速度的測試包括是通過終端gradle命令的--profile
這是原來:
這是現在:
總結: 技術上, 構建有了顯著提升(縮短了20多秒), 但感官上, 從1min10s到48s, 給人感覺還是慢, 升級android-gradle-plugin也還是慢(因為跟gradle本身沒多大關係, 而取決於android-gradle-plugin), 比android的”ant”構建, facebook的”buck”構建慢很多;
小弟感覺android studio 2.0最主要的不是通過提高gradle-plugin的效能來提速的, 而是想通過instant run(目前好像還沒做好, 有時改動燒進去沒變化), 即在install apk上, 少做拷貝apk(越大越慢)然後pm install -r, 取而代之的是, 保持一個連線, 原生代碼的變化通過差異索引得知, 然後通過dexclassloader重新載入, 換句話說, 很像熱補丁代替靜態升級的做法.
對我來說, ant太過低階(類似makefile, 所有要親力親為), buck要求在每個目錄下都有配置檔案, 學習成本也不便宜, 且目前還不支援windows, 這兩種都不是最好的選擇, gradle表現不給力, 從未知性上, 以及相容android studio, intellij idea, eclipse adt的角度, 想再看看maven方式是否能編譯的快些, 只希望比gradle快些就行, 結果讓人很失望, 不僅沒快多少, 而且損失也很明顯: 不能用ide的一鍵按鈕安裝, 而是手動install apk; 非官方構建, 需要維護兩套相容構建配置;
其實還一個建設性的方法: 通過劃分多模組/多apk來減少依賴的反覆構建;
另外我注意到jumbo mode這個概念, 它是指dx –force-jumbo, 原文意思是:
it is about the opcode such that “op vAA, [email protected]” versus “op vAA, [email protected]”, 32 bits versus 16 bit.
將其設定為true, 可解決以下警告:
AGPBI: {"kind":"SIMPLE","text":"UNEXPECTED TOP-LEVEL EXCEPTION:","position":{},"original":"UNEXPECTED TOP-LEVEL EXCEPTION:"}
AGPBI: {"kind":"SIMPLE","text":"com.android.dex.DexIndexOverflowException: Cannot merge new index 65772 into a non-jumbo instruction!","position":{},"original":"com.android.dex.DexIndexOverflowException: Cannot merge new index 65772 into a non-jumbo instruction!"}
AGPBI: {"kind":"SIMPLE","text":"\tat com.android.dx.merge.InstructionTransformer.jumboCheck(InstructionTransformer.java:109)","position":{},"original":"\tat com.android.dx.merge.InstructionTransformer.jumboCheck(InstructionTransformer.java:109)"}
AGPBI: {"kind":"SIMPLE","text":"\tat com.android.dx.merge.InstructionTransformer.access$800(InstructionTransformer.java:26)","position":{},"original":"\tat com.android.dx.merge.InstructionTransformer.access$800(InstructionTransformer.java:26)"}
AGPBI: {"kind":"SIMPLE","text":"\tat com.android.dx.merge.InstructionTransformer$StringVisitor.visit(InstructionTransformer.java:72)","position":{},"original":"\tat com.android.dx.merge.InstructionTransformer$StringVisitor.visit(InstructionTransformer.java:72)"}
AGPBI: {"kind":"SIMPLE","text":"\tat com.android.dx.io.CodeReader.callVisit(CodeReader.java:114)","position":{},"original":"\tat com.android.dx.io.CodeReader.callVisit(CodeReader.java:114)"}
AGPBI: {"kind":"SIMPLE","text":"\tat com.android.dx.io.CodeReader.visitAll(CodeReader.java:89)","position":{},"original":"\tat com.android.dx.io.CodeReader.visitAll(CodeReader.java:89)"}
AGPBI: {"kind":"SIMPLE","text":"\tat com.android.dx.merge.InstructionTransformer.transform(InstructionTransformer.java:49)","position":{},"original":"\tat com.android.dx.merge.InstructionTransformer.transform(InstructionTransformer.java:49)"}
...
哎~ 看來android gradle plugin, 目前只能忍著吧.
後記:
dexOptions.threadCount為什麼不起作用, 原來原始碼中說有問題給注掉了, 坑爹啊! 見com.android.builder.core.AndroidBuilder.java
dexDebug執行的具體命令如下, 注意每次哪怕程式碼有一行更新, 其後構建都刪掉重建allclasses.jar[它是在:app:packageAllDebugClassesForMultiDex任務中生成的, 另一個jar包componentClasses.jar則在:app:shrinkDebugMultiDexComponents任務中生成], 然後將這個jar搞成一個或多個dex, 目前沒有任何重用, dexOptions中的增量也只對非multi-dex方案有效, 這是瓶頸所在啊!
java -Xmx4g -Dfile.encoding=UTF-8 -Duser.country=CN -Duser.language=zh -Duser.variant -cp ${ANDROID_SDK_HOME}/build-tools/23.0.1/lib/dx.jar com.android.dx.command.Main --dex --verbose --num-threads=4 --multi-dex --main-dex-list ${PROJECT_ROOT}/app/build/intermediates/multi-dex/debug/maindexlist.txt --output ${PROJECT_ROOT}/app/build/intermediates/dex/debug ${PROJECT_ROOT}/app/build/intermediates/multi-dex/debug/allclasses.jar
發表一點感想. 僅對我來說, gradle的multi-dex方案感覺很戲謔, 因為它, dex的生成沒法做到增量, 還要通過切換minsdkversion來換取構建速度的微弱提升, 它是你當初沒有做好多專案模組化外掛化, 然後被65535問題驚醒時所做的懶人決定, 你渴望讓構建工具替你分出多個dex, 你渴望不要再有其他麻煩, 它也確實如你意, 這麼做了, 可是它要建立在allclasses.jar這樣一個瓶頸上, 而且它的根本卻還是要靠dexclassloader, 於是你不得不用更多的編譯時間一天天償還你當初的懶惰, 彌補你係統前瞻性設計的缺乏;
網上查到可以通過這樣的方式在除錯時加快構建速度, 各位看官可以品嚐: