前言

最近因為項目的編譯速度越來越慢,嚴重到有時候甚至接近十分鐘才能完成一次完整編譯,就決定對著官方文檔對Gradle進行一番優化。優化完成後果然構建速度得到大幅提升,遂在此記錄

如何開始

由於是對照官方文檔並結合實際項目進行優化,所以某些細節或者項目中沒有用到的點就簡略帶過

保持工具處於最新狀態

Android Studio 和 SDK 工具?

link.juejin.im

Android Plugin for Gradle?

link.juejin.im

為開發創建構建變體

即為生產環境與開發環境分開配置productFlavors

避免編譯不必要的資源

您可以僅為dev的productFlavors指定一個語言資源和屏幕密度

productFlavors {
dev {
resConfigs "en", "xxhdpi"
}
...
}

為您的調試構建停用 Crashlytics(Fabric)

一般是在debug時停用

android {
...
buildTypes {
debug {
ext.enableCrashlytics = false
}
}

將靜態構建配置值與調試構建結合使用

始終為進入 manifest 文件的屬性使用靜態/硬編碼值,或者為您的調試構建類型使用資源文件。如果您的 manifest 文件或應用資源中的值需要隨著每一個構建更新,Instant Run 將無法執行代碼交換 - 它必須構建和安裝新的 APK

例如,在您每次想要運行更改時,使用動態版本代碼、版本名稱、資源或任何其他可以更改manifest 文件的構建邏輯都需要一個完整的 APK 構建 - 即使實際更改僅需要一個熱交換,也是如此。如果您的構建配置需要此類動態屬性,那麼將其隔離到您的發布構建變體中並讓值對您的調試構建保持靜態

簡單點來說,就是如果gradle構建中使用了動態構建配置,那麼Instant Run就無法起到應有的作用,與直接構建新的APK沒有任何區別

下面是例子

int MILLIS_IN_MINUTE = 1000 * 60
int minutesSinceEpoch = System.currentTimeMillis() / MILLIS_IN_MINUTE

android {
...
defaultConfig {
versionCode 1
versionName "1.0"
...
}

applicationVariants.all { variant ->
if (variant.buildType.name == "release") {
variant.mergedFlavor.versionCode = minutesSinceEpoch;
variant.mergedFlavor.versionName = minutesSinceEpoch + "-" + variant.flavorName;
}
}
}

使用靜態依賴項版本

build.gradle文件中聲明依賴項時,您應當避免在結尾將版本號與加號一起使用,例如com.android.tools.build:gradle:2.+ 使用動態版本號可能導致意外版本更新和難以解析版本差異,並因 Gradle 檢查有無更新而減慢構建速度。您應改為使用靜態/硬編碼版本號

這個大家都懂,就不多說了

啟用離線模式

如果您的網路連接速度比較慢,那麼在 Gradle 嘗試使用網路資源解析依賴項時,您的構建時間可能會延長。您可以指示 Gradle 僅使用它已經緩存到本地的工件來避免使用網路資源

這個也是很常見的加快構建速度的方式,尤其是在國內,使用離線模式後構建速度可以大大提升。但是離線模式在每次引用新的依賴時會找不到依賴,所以新項目的話,還是能不用就不要用吧

啟用按需配置

為了讓 Gradle 準確瞭解如何構建您的應用,構建系統會在每個構建前在項目中配置所有模塊以及這些模塊的依賴項(即使您正在構建和測試一個模塊,也是如此)。這會減慢大型多模塊項目的構建進程

注意:按需配置在新版的Android Studio中已經沒有了

啟用並行化編譯

在嘗試按需配置的過程中發現Compile independent modules in parallel這個選項,查詢一番後發現是使用並行化編譯,能提高編譯速度,勾選即可

在模塊化開發中,啟用並行化編譯提高編譯速度更為顯著

創建庫模塊

在應用中查找您可以轉換成 Android 庫模塊的代碼。通過這種方式將您的代碼模塊化可以讓構建系統僅編譯您修改的模塊,並緩存這些輸出以用於未來構建。這種方式也會讓按需配置和並行項目執行更有效(如果您啟用這些功能)

就是讓你把項目模塊化,不再贅述了

為自定義構建邏輯創建任務

在您創建構建分析後,如果分析顯示構建時間中相當大的一部分用在了「配置項目」階段,請檢查 build.gradle 腳本並查找您可以添加到自定義 Gradle 任務中的代碼。將某個構建邏輯移動到任務中後,它僅會在需要時運行,可以為後續構建緩存結果,並且該構建邏輯將有資格並行運行(如果您啟用並行項目執行)。要了解詳情,請閱讀官方 Gradle 文檔。

就是讓你把一些不是必需執行的構建轉移到Task中執行,比如某個不需要在Debug時執行的構建

配置 dexOptions 和啟用庫預 dexing

啟用這些配置可能加快構建,但是按官網說的,

您應當遞增這些設置的值來試驗它們並通過分析您的構建觀察效果。如果您向進程分配過多的資源,性能可能會下降

所以這個還是得自己判斷是否開啟。至於具體的配置對應的含義,官網已經寫得很清楚了,就直接發出來

  • preDexLibraries聲明是否預 dex 庫依賴項以加快您的增量構建速度。由於此功能可能減慢您的乾淨構建的速度,您可能需要為持續性集成伺服器停用此功能。
  • maxProcessCount設置運行 dex-in-process 時要使用的最大線程數量。默認值為 4。
  • javaMaxHeapSize設置 DEX 編譯器的最大堆大小。不過,您應當增加 Gradle 的堆大小(啟用 dex-in-process 時,將與 DEX 編譯器共享),而不是設置此屬性。

增加 Gradle 的堆大小並啟用 dex-in-process

在項目的 gradle.properties 文件中將 Gradle 的堆大小設置為 2048 MB:

org.gradle.jvmargs = -Xmx2048m

將圖像轉換成 WebP

這也是老生常談了,Android Studio裏就能直接轉換,不過我在項目中並沒有這麼幹

停用 PNG 處理

如果您無法(或者不想)將 PNG 圖像轉換成 WebP,仍可以通過在每次構建應用時停用自動圖像壓縮的方式加快構建速度。要停用此優化,請將以下代碼添加到您的 build.gradle 文件中:

android {
...
aaptOptions {
cruncherEnabled false
}
}

由於構建類型或產品風味不定義此屬性,在構建發布版本的應用時,您需要將此屬性手動設置為 true

開啟了這個,好像編譯速度又能快一個臺階

啟用 Instant Run

前面提到過了,前提是腳本中盡量使用靜態依賴。由於目前項目中動態依賴寫得太多,所以這個暫時還是沒有用起來

啟用構建緩存

使用 Android 插件 2.3.0 及更高版本的新項目在默認情況下會啟用構建緩存(除非您明確停用構建緩存)

停用註解處理器

使用註解處理器(Annotation-Processing-Tool)時,增量 Java 編譯處於停用狀態。如果可以,請嘗試避免使用註解處理器,以便在不同構建之間僅編譯您修改的類

那麼多第三方庫用註解寫的,一般情況下不太可能停用註解處理但是不使用Butterknife轉而使用findViewById確實能提高編譯速度,因為避免了生成對應的註解類。建議在子元素不多或者頻繁生成時候直接findViewById

尾聲

其實就是一篇官網構建速度優化的讀後感,但按照提示一步步來,構建速度提升確實比較明顯。下一篇會講講如何使用構建分析工具來分析構建速度慢的原因

現組建了一個Android的大圈子,內有大神小白起飛,圈內會不定時更新一些Android中高級的進階資料,歡迎大家帶著技術問題來討論,共同成長進步!(包含資深UI工程師,Android底層開發工程師,Android架構師,原生性能優化及混合優化,flutter專精);

歡迎點贊+私信我 資料 即可加入~

推薦閱讀:

相關文章