建構時間越長,開發過程就越慢。本頁提供一些技巧,幫助您解決建構速度瓶頸。
提高建構速度的一般程序如下:
在開發應用程式時,您應該盡可能部署至搭載 Android 7.0 (API 級別 24) 以上版本的裝置。這是因為較新版的 Android 平台在推送更新到應用程式時,可以提供更好的作業機制,例如 Android 執行階段 (ART) 以及對多個 DEX 檔案的原生支援。
注意:第一次進行建構清理後,您可能會發現後續的清理和漸進式建構速度變快許多 (即使並未採行本頁所述的任何最佳化措施)。這是因為 Gradle Daemon 有一個增加效能的「暖身」時間,這與其他 JVM 過程類似。
最佳化建構設定
請按照下列提示來提升 Android Studio 專案的建構速度。
確認工具為最新版本
幾乎每一次更新後,Android 工具都會實現建構最佳化並收到新功能,而且本頁中的一些提示也假設您使用的是最新版本。為了充分利用最新的最佳化功能,請確保以下項目為最新版本:
建立用於開發的建構變數
在開發應用程式時,不需要準備發布應用程式時所需的許多設定。啟用不必要的建構程序會拖慢您的漸進式和清理建構速度,因此設定一個建構變數,僅保留開發應用程式時所需的建構設定即可。下列範例建立了「dev」版本和「prod」版本 (針對您的發布版本設定):
Groovy
android { ... defaultConfig {...} buildTypes {...} productFlavors { // When building a variant that uses this flavor, the following configurations // override those in the defaultConfig block. dev { // To avoid using legacy multidex when building from the command line, // set minSdkVersion to 21 or higher. When using Android Studio 2.3 or higher, // the build automatically avoids legacy multidex when deploying to a device running // API level 21 or higher—regardless of what you set as your minSdkVersion. minSdkVersion 21 versionNameSuffix "-dev" applicationIdSuffix '.dev' } prod { // If you've configured the defaultConfig block for the release version of // your app, you can leave this block empty and Gradle uses configurations in // the defaultConfig block instead. You still need to create this flavor. // Otherwise, all variants use the "dev" flavor configurations. } } }
Kotlin
android { ... defaultConfig {...} buildTypes {...} productFlavors { // When building a variant that uses this flavor, the following configurations // override those in the defaultConfig block. create("dev") { // To avoid using legacy multidex when building from the command line, // set minSdkVersion to 21 or higher. When using Android Studio 2.3 or higher, // the build automatically avoids legacy multidex when deploying to a device running // API level 21 or higher—regardless of what you set as your minSdkVersion. minSdkVersion(21) versionNameSuffix = "-dev" applicationIdSuffix = ".dev" } create("prod") { // If you've configured the defaultConfig block for the release version of // your app, you can leave this block empty and Gradle uses configurations in // the defaultConfig block instead. You still need to create this flavor. // Otherwise, all variants use the "dev" flavor configurations. } } }
如果建構設定已使用變種版本建立不同版本的應用程式,您就可以使用版本維度將「dev」和「prod」設定與這些版本結合起來。舉例來說,如果您已設定「demo」和「full」版本,便可使用下列範例設定建立組合版本,例如「devDemo」和「prodFull」:
Groovy
android { ... defaultConfig {...} buildTypes {...} // Specifies the flavor dimensions you want to use. The order in which you // list each dimension determines its priority, from highest to lowest, // when Gradle merges variant sources and configurations. You must assign // each product flavor you configure to one of the flavor dimensions. flavorDimensions "stage", "mode" productFlavors { dev { dimension "stage" minSdkVersion 21 versionNameSuffix "-dev" applicationIdSuffix '.dev' ... } prod { dimension "stage" ... } demo { dimension "mode" ... } full { dimension "mode" ... } } }
Kotlin
android { ... defaultConfig {...} buildTypes {...} // Specifies the flavor dimensions you want to use. The order in which you // list each dimension determines its priority, from highest to lowest, // when Gradle merges variant sources and configurations. You must assign // each product flavor you configure to one of the flavor dimensions. flavorDimensions("stage", "mode") productFlavors { create("dev") { dimension = "stage" minSdkVersion(21) versionNameSuffix = "-dev" applicationIdSuffix = ".dev" ... } create("prod") { dimension = "stage" ... } create("demo") { dimension = "mode" ... } create("full") { dimension = "mode" ... } } }
單一變化版本專案同步處理
將專案與建構設定同步處理是相當重要的一步,這可以讓 Android Studio 瞭解您的專案結構。不過,對大型專案而言,這項程序可能相當耗時。如果您的專案使用多個建構變數,則 Android Studio 只會同步化您目前所選的變化版本,以便最佳化處理專案同步作業。
所有專案都會預設為啟用這項最佳化功能,但在 Android Studio 4.2 以上版本無法設定該功能。
如要手動啟用這項最佳化功能,您必須搭配使用 Android Studio 3.3 以上版本及 Android Gradle 外掛程式 3.3.0 以上版本。依序點選「File」>「Settings」>「Experimental」>「Gradle」 (如使用 Mac 電腦則依序按一下「Android Studio」>「Preferences」>「Experimental」>「Gradle」),然後勾選「Only sync the active variant」核取方塊。
注意:此項最佳化功能可完整支援包含 Java 和 C++ 語言的專案,也可對 Kotlin 語言提供部分支援。對包含 Kotlin 內容的專案啟用最佳化作業時,Gradle 同步處理會在內部改回使用完整變化版本。
避免編譯不必要的資源
請勿編譯及封裝非測試中的資源 (例如其他語言本地化和螢幕密度資源)。方法很簡單,只需為您的「dev」版本指定一種語言資源和螢幕密度即可,如下列範例所示:
Groovy
android { ... productFlavors { dev { ... // The following configuration limits the "dev" flavor to using // English stringresources and xxhdpi screen-density resources. resConfigs "en", "xxhdpi" } ... } }
Kotlin
android { ... productFlavors { create("dev") { ... // The following configuration limits the "dev" flavor to using // English stringresources and xxhdpi screen-density resources. resConfigs("en", "xxhdpi") } ... } }
為偵錯版本停用 Crashlytics
如果您無需執行 Crashlytics 報告,請按照下列步驟停用外掛程式以加快偵錯建建構速度:
Groovy
android { ... buildTypes { debug { ext.enableCrashlytics = false } } }
Kotlin
android { ... buildTypes { getByName("debug") { extra["enableCrashlytics"] = false } } }
此外,您還必須透過變更應用程式中初始化 Fabric 支援的方式,在執行階段停用 Crashlytics 套件,如下所示:
Kotlin
// Initializes Fabric for builds that don't use the debug build type. Crashlytics.Builder() .core(CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build()) .build() .also { crashlyticsKit -> Fabric.with(this, crashlyticsKit) }
Java
// Initializes Fabric for builds that don't use the debug build type. Crashlytics crashlyticsKit = new Crashlytics.Builder() .core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build()) .build(); Fabric.with(this, crashlyticsKit);
停用建構 ID 自動產生功能
如要將 Crashlytics 與偵錯建構一起使用,您仍可透過阻止 Crashlytics 在每次建構期間使用自己專屬的建構 ID 更新應用程式資源,來加快漸進式建構速度。由於這個建構 ID 會儲存在資訊清單參照的資源檔案中,因此停用建構 ID 自動產生功能也可讓您對偵錯建構使用「套用變更」設定和 Crashlytics。
為避免 Crashlytics 自動更新建構 ID,請將以下程式碼新增到您的 build.gradle
檔案中:
Groovy
android { ... buildTypes { debug { ext.alwaysUpdateBuildId = false } } }
Kotlin
android { ... buildTypes { getByName("debug") { extra["alwaysUpdateBuildId"] = false } } }
如要進一步瞭解使用 Crashlytics 時如何最佳化建構,請參閱官方說明文件。
對偵錯版本使用靜態建構設定值
對於偵錯建構類型,務必對資訊清單檔案或資源檔案中的屬性使用靜態/硬式編碼值。
舉例來說,如果您使用動態版本代碼、版本名稱、資源或任何其他變更資訊清單檔案的建構邏輯,則每次執行變更時都必須完成完整的應用程式建構,即使實際的變更可能只需要熱交換。如果您的建構設定需要這類動態屬性,請將這些屬性與發布建構變數區隔開來,並保持偵錯建構的值為靜態。有關範例,請參閱下方所示的 build.gradle
檔案。
Groovy
int MILLIS_IN_MINUTE = 1000 * 60 int minutesSinceEpoch = System.currentTimeMillis() / MILLIS_IN_MINUTE android { ... defaultConfig { // Making either of these two values dynamic in the defaultConfig will // require a full app build and reinstallation because the AndroidManifest.xml // must be updated. versionCode 1 versionName "1.0" ... } // The defaultConfig values above are fixed, so your incremental builds don't // need to rebuild the manifest (and therefore the whole app, slowing build times). // But for release builds, it's okay. So the following script iterates through // all the known variants, finds those that are "release" build types, and // changes those properties to something dynamic. applicationVariants.all { variant -> if (variant.buildType.name == "release") { variant.mergedFlavor.versionCode = minutesSinceEpoch; variant.mergedFlavor.versionName = minutesSinceEpoch + "-" + variant.flavorName; } } }
Kotlin
val MILLIS_IN_MINUTE = 1000 * 60 val minutesSinceEpoch = System.currentTimeMillis() / MILLIS_IN_MINUTE android { ... defaultConfig { // Making either of these two values dynamic in the defaultConfig will // require a full app build and reinstallation because the AndroidManifest.xml // must be updated. versionCode = 1 versionName = "1.0" ... } // The defaultConfig values above are fixed, so your incremental builds don't // need to rebuild the manifest (and therefore the whole app, slowing build times). // But for release builds, it's okay. So the following script iterates through // all the known variants, finds those that are "release" build types, and // changes those properties to something dynamic. applicationVariants.forEach { 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 檢查更新造成建構速度變慢。請使用靜態/硬式編碼的版本號碼。
建立程式庫模組
在應用程式中,找出可轉換為 Android 程式庫模組的程式碼。以這種方式模組化程式碼可讓建構系統僅編譯您修改的模組,並快取這些輸出內容以供日後建構使用。這樣做還能提高平行專案執行的效率 (在您啟用最佳化功能後)。
建立自訂建構邏輯的工作
建立建構設定檔後,如果顯示在「設定專案」階段花費的建構時間相對較長,請審查您的 build.gradle
指令碼,然後尋找您可以在自訂 Gradle 工作中加入的程式碼。將部分建構邏輯移至工作後,將只在需要時執行它,並可快取結果供後續建構使用,而且這些建構邏輯也符合平行執行的資格 (如果您啟用平行專案執行)。詳情請參閱 Gradle 官方說明文件。
提示:如果您的建構包含大量自訂工作,建議您建立自訂工作類別來整理 build.gradle
檔案。將類別新增至 project-root/buildSrc/src/main/groovy/
目錄後,Gradle 會自動將這些類別加入專案內所有 build.gradle
檔案的類別路徑中。
將圖片轉換為 WebP
WebP 是一種圖片檔案格式,可提供有損壓縮 (例如 JPEG) 和透明度 (例如 PNG),但壓縮效果比 JPEG 或 PNG 更好。不需要執行建構時間壓縮,只要縮減圖片檔尺寸,就可以加快建構速度,特別是當應用程式使用大量圖片資源時,更是如此。但是,在解壓 WebP 圖片時,您可能會注意到裝置的 CPU 使用量有些微增加。使用 Android Studio,您可以輕鬆將圖片轉換為 WebP。
停用 PNG 壓縮功能
如果無法 (或不想) 將 PNG 圖片轉換為 WebP,可以在每次建構應用程式時停用自動圖片壓縮功能,藉此加快建構速度。如果您使用的是 Android 外掛程式 3.0.0 以上版本,則只有「偵錯」建構類型會預設停用 PNG 壓縮功能。如要停用其他建構類型的這項最佳化功能,可將以下內容新增到您的 build.gradle
檔案中:
Groovy
android { buildTypes { release { // Disables PNG crunching for the release build type. crunchPngs false } } // If you're using an older version of the plugin, use the // following: // aaptOptions { // cruncherEnabled false // } }
Kotlin
android { buildTypes { getByName("release") { // Disables PNG crunching for the release build type. isCrunchPngs = false } } // If you're using an older version of the plugin, use the // following: // aaptOptions { // cruncherEnabled = false // } }
由於建構類型或變種版本不會定義這個屬性,因此您必須在建構應用程式發布版本時手動將這個屬性設為 true
。
使用漸進式註解處理工具
Android Gradle 外掛程式 3.3.0 以上版本可改善對漸進式註解處理功能的支援。因此,如要加快漸進式建構速度,建議您更新 Android Gradle 外掛程式,並盡可能只使用漸進式註解處理工具。
注意:這項功能與 Gradle 4.10.1 以上版本相容,但 Gradle 5.1 版除外 (請參閱 Gradle 問題 #8194)。
在開始使用前,請參閱支援漸進式註解處理的熱門註解處理工具表格。如需更完整的清單,請參閱熱門註解處理工具的支援狀態。有些註解處理工具可能需要執行其他步驟來啟用最佳化功能,因此請務必詳閱各註解處理工具的說明文件。
此外,如果您在應用程式中使用 Kotlin,則必須使用 kapt 1.3.30 以上版本,以支援適用於 Kotlin 程式碼的漸進式註解處理工具。請務必詳閱官方說明文件,瞭解是否必須手動啟用這項行為。請注意,如果您不得不使用一個或多個不支援漸進式建構的註解處理工具,則註解處理作業不會呈漸進式。不過,如果您的專案使用 kapt,Java 編譯仍是漸進式的。
漸進式註解處理工具支援
專案名稱 | 註解處理工具類別名稱 | 支援開始日期 |
---|---|---|
DataBinding | android.databinding.annotationprocessor.ProcessDataBinding | AGP 3.5 |
Room | androidx.room.RoomProcessor | 2.3.0-alpha02
2.20:使用 room.incremental 選項。 |
ButterKnife | butterknife.compiler.ButterKnifeProcessor | 10.2.0 |
Glide | com.bumptech.glide.annotation.compiler.GlideAnnotationProcessor | 4.9.0 |
Dagger | dagger.internal.codegen.ComponentProcessor | 2.18 |
Lifecycle | androidx.lifecycle.LifecycleProcessor | 2.2.0-alpha02 |
AutoService | com.google.auto.service.processor.AutoServiceProcessor | 1.0-rc7 |
Dagger | dagger.android.processor.AndroidProcessor | 2.18 |
Realm | io.realm.processor.RealmProcessor | 5.11.0 |
Lombok | lombok.launch.AnnotationProcessorHider$AnnotationProcessor | 1.16.22 |
Lombok | lombok.launch.AnnotationProcessorHider$ClaimingProcessor | 1.16.22 |
設定 JVM 垃圾收集器
您可以設定 Gradle 使用的最佳 JVM 垃圾收集器,藉此提升建構效能。JDK 8 已預設設定為使用平行垃圾收集器,而 JDK 9 以上版本則設定為使用 G1 垃圾收集器。
為了提升建構效能,我們建議您使用平行垃圾收集器測試 Gradle 建構。在 gradle.properties
中設定:
org.gradle.jvmargs=-XX:+UseParallelGC
如果該欄位已設有其他選項,則新增一個新選項:
org.gradle.jvmargs=-Xmx1536m -XX:+UseParallelGC
如要使用其他設定測量建構速度,請參閱剖析建構。
使用非遞移 R 類別
您應使用非遞移 R 類別,以便更快建構具有多個模組的應用程式。這樣可以確保每個模組的 R 類別只包含其本身資源的參照,而不必提取其依附元件的參照,避免資源重複的情形,進而加快建構速度,同時享有避免編譯帶來的對應好處。
從 Android Studio Bumblebee 開始,新專案會預設啟用非遞移 R 類別。對於使用舊版 Studio 建立的專案,若要將其更新為使用非遞移 R 類別,只要前往「Refactor」>「Migrate to Non-Transitive R Classes」進行設定即可。
如要進一步瞭解應用程式資源和 R 類別,請參閱應用程式資源總覽。
停用 Jetifier 旗標
多數專案都會直接使用 AndroidX 程式庫,因此您可以移除 Jetifier 旗標以提升建構效能。如要移除 Jetifier 檢查,請在 gradle.properties
檔案中設定 android.enableJetifier=false
。版本分析器可以執行檢查,確認是否可安全移除該旗標,確保您的專案能有更佳的建構效能,並從未維護的 Android 支援資料庫中遷移。如要進一步瞭解版本分析器,請參閱排解建構效能問題一文。
使用設定快取 (實驗功能)
設定快取是一項實驗功能,可讓 Gradle 記錄建構工作圖表的相關資訊,並在後續建構中重複使用,從而無需重新設定整個建構作業。如要啟用設定快取,請按照下列步驟操作:
- 確認所有專案外掛程式都相容。使用版本分析器檢查您的專案是否與設定快取相容。這會執行一系列測試建構,以判斷是否能為專案啟用這項功能。您也可以查看問題 #13490,瞭解支援的外掛程式清單。
將下列程式碼新增至
gradle.properties
檔案。org.gradle.unsafe.configuration-cache=true # Use this flag sparingly, in case some of the plugins are not fully compatible org.gradle.unsafe.configuration-cache-problems=warn
- 啟用設定快取後,首次執行專案時,建構輸出內容應會顯示
Calculating task graph as no configuration cache is available for tasks
。後續執行時,建構輸出內容應顯示Reusing configuration cache
。