Android Note - 構建速度優化

前言

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

如何開始

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

保持工具處於最新狀態

Android Studio 和 SDK 工具android

Android Plugin for Gradle緩存

爲開發建立構建變體

即爲生產環境與開發環境分開配置productFlavorsbash

避免編譯沒必要要的資源

您能夠僅爲devproductFlavors指定一個語言資源和屏幕密度服務器

productFlavors {
    dev {
      resConfigs "en", "xxhdpi"
    }
    ...
}
複製代碼

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

通常是在debug時停用網絡

android {
  ...
  buildTypes {
    debug {
      ext.enableCrashlytics = false
    }
}
複製代碼

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

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

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

簡單點來講,就是若是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

尾聲

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

Optimize your build speed

相關文章
相關標籤/搜索