對建構速度進行最佳化調整

建構時間越長,開發過程就越慢。本頁提供一些技巧,幫助您解決建構速度瓶頸。

提高建構速度的一般程序如下:

  1. 利用幾個步驟執行最佳化建構設定,為大多數 Android Studio 專案帶來立竿見影的效果。
  2. 剖析您的建構,找出並診斷您的專案或工作站可能遇到的一些棘手瓶頸。

在開發應用程式時,您應該盡可能部署至搭載 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 編譯仍是漸進式的。

漸進式註解處理工具支援

專案名稱註解處理工具類別名稱支援開始日期
DataBindingandroid.databinding.annotationprocessor.ProcessDataBindingAGP 3.5
Roomandroidx.room.RoomProcessor2.3.0-alpha02

2.20:使用 room.incremental 選項
ButterKnifebutterknife.compiler.ButterKnifeProcessor10.2.0
Glidecom.bumptech.glide.annotation.compiler.GlideAnnotationProcessor4.9.0
Daggerdagger.internal.codegen.ComponentProcessor2.18
Lifecycleandroidx.lifecycle.LifecycleProcessor2.2.0-alpha02
AutoServicecom.google.auto.service.processor.AutoServiceProcessor1.0-rc7
Daggerdagger.android.processor.AndroidProcessor2.18
Realmio.realm.processor.RealmProcessor5.11.0
Lomboklombok.launch.AnnotationProcessorHider$AnnotationProcessor1.16.22
Lomboklombok.launch.AnnotationProcessorHider$ClaimingProcessor1.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 記錄建構工作圖表的相關資訊,並在後續建構中重複使用,從而無需重新設定整個建構作業。如要啟用設定快取,請按照下列步驟操作:

  1. 確認所有專案外掛程式都相容。使用版本分析器檢查您的專案是否與設定快取相容。這會執行一系列測試建構,以判斷是否能為專案啟用這項功能。您也可以查看問題 #13490,瞭解支援的外掛程式清單。
  2. 將下列程式碼新增至 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

  3. 啟用設定快取後,首次執行專案時,建構輸出內容應會顯示 Calculating task graph as no configuration cache is available for tasks。後續執行時,建構輸出內容應顯示 Reusing configuration cache
如要進一步瞭解設定快取,請參閱「設定快取深入探索」一文,以及 Gradle 設定快取說明文件