提升建構速度

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

改善應用程式建構速度的一般程序如下:

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

在開發應用程式時,您應盡可能部署至搭載 Android 7.0 (API 級別 24) 以上版本的裝置。這是因為較新版的 Android 平台在推送更新到應用程式時,可以提供更好的作業機制,例如 Android 執行階段 (ART) 以及對多個 DEX 檔案的原生支援。

注意:第一次進行建構清理後,您可能會發現後續的清理和漸進式建構速度變快許多,即使並未採行本頁所述的任何最佳化措施。這是因為 Gradle Daemon 有一個增加效能的「暖身」時間,這與其他 JVM 過程類似。

最佳化建構設定

請按照下列提示來提升 Android Studio 專案的建構速度。

確認工具為最新版本

幾乎每一次更新後,Android 工具都會實現建構最佳化並收到新功能。本頁中的某些提示假設您使用的是最新版本。為了充分利用最新的最佳化功能,請確保以下項目為最新版本:

使用 KSP,而非 kapt

Kotlin 註解處理工具 (kapt) 比 Kotlin Symbol Processor (KSP) 慢許多。如要編寫已加註的 Kotlin 來源,並使用用於處理註解的工具 (例如 Room),請遷移至 KSP

避免編譯不必要的資源

請勿編譯及封裝非測試中的資源,例如其他語言本地化和螢幕密度資源。而只需為您的「dev」版本指定一種語言資源和螢幕密度即可,如下列範例所示:

Groovy

android {
    ...
    productFlavors {
        dev {
            ...
            // The following configuration limits the "dev" flavor to using
            // English stringresources and xxhdpi screen-density resources.
            resourceConfigurations "en", "xxhdpi"
        }
        ...
    }
}

Kotlin

android {
    ...
    productFlavors {
        create("dev") {
            ...
            // The following configuration limits the "dev" flavor to using
            // English stringresources and xxhdpi screen-density resources.
            resourceConfigurations("en", "xxhdpi")
        }
        ...
    }
}

嘗試將 Gradle 外掛程式入口網站放在最後

在 Android 中,所有外掛程式都位於 google()mavenCentral() 存放區。但是,您的版本可能需要使用 gradlePluginPortal() 服務解析的第三方外掛程式。

Gradle 會依照宣告的順序搜尋存放區,因此如果優先列出的存放區含有大部分的外掛程式,建構效能就會有所提升。因此,您可以嘗試將 gradlePluginPortal() 項目放到 settings.gradle 檔案中存放區區塊的最後位置。在多數情況下,這可盡量減少冗餘外掛程式的搜尋次數,並提升建構速度。

如要進一步瞭解 Gradle 如何瀏覽多個存放區,請參閱 Gradle 文件中的宣告多個存放區一文。

對偵錯版本使用靜態建構設定值

對於偵錯建構類型,務必對資訊清單檔案或資源檔案中的屬性使用靜態值。

如果您使用動態版本代碼、版本名稱、資源或其他可變更資訊清單檔案的建構邏輯,則每次執行變更都必須完成完整的應用程式建構作業,即使實際的變更可能只需要熱切換也一樣。如果您的建構設定需要這類動態屬性,請將這些屬性與發布建構變數區隔開來,並保持偵錯建構作業的值為靜態,如以下範例所示:

  ...
  // Use a filter to apply onVariants() to a subset of the variants.
  onVariants(selector().withBuildType("release")) { variant ->
      // Because an app module can have multiple outputs when using multi-APK, versionCode
      // is only available on the variant output.
      // Gather the output when we are in single mode and there is no multi-APK.
      val mainOutput = variant.outputs.single { it.outputType == OutputType.SINGLE }

      // Create the version code generating task.
      val versionCodeTask = project.tasks.register("computeVersionCodeFor${variant.name}", VersionCodeTask::class.java) {
          it.outputFile.set(project.layout.buildDirectory.file("versionCode${variant.name}.txt"))
      }

      // Wire the version code from the task output.
      // map will create a lazy Provider that:
      // 1. Runs just before the consumer(s), ensuring that the producer (VersionCodeTask) has run
      //    and therefore the file is created.
      // 2. Contains task dependency information so that the consumer(s) run after the producer.
      mainOutput.versionCode.set(versionCodeTask.flatMap { it.outputFile.map { it.asFile.readText().toInt() } })
  }
  ...

  abstract class VersionCodeTask : DefaultTask() {

    @get:OutputFile
    abstract val outputFile: RegularFileProperty

    @TaskAction
    fun action() {
        outputFile.get().asFile.writeText("1.1.1")
    }
  }

如要瞭解在專案中設定動態版本代碼的方法,請參閱 GitHub 上的「setVersionsFromTask recipe」。

使用靜態依附元件版本

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)。WebP 的壓縮效果比 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
        }
    }
}

Kotlin

android {
    buildTypes {
        getByName("release") {
            // Disables PNG crunching for the "release" build type.
            isCrunchPngs = false
        }
    }
}

由於建構類型或變種版本不會定義這個屬性,因此您必須在建構應用程式發布版本時,手動將這個屬性設為「true」。

使用 JVM 平行垃圾收集器進行實驗

您可以設定 Gradle 使用的最佳 JVM 垃圾收集器,藉此提升建構效能。JDK 8 已預設設定為使用平行垃圾收集器,而 JDK 9 及以上版本則設定為使用 G1 垃圾收集器

為了提升建構效能,我們建議您使用平行垃圾收集器測試 Gradle 建構。在 gradle.properties 中設定:

org.gradle.jvmargs=-XX:+UseParallelGC

如果該欄位已設有其他選項,則新增一個新選項:

org.gradle.jvmargs=-Xmx1536m -XX:+UseParallelGC

如要使用其他設定測量建構速度,請參閱剖析版本一文。

增加 JVM 堆積大小

如果發現建構速度緩慢,而且在建構分析器結果中,垃圾收集需要超過 15% 的建構時間,則應增加 Java 虛擬機器 (JVM) 堆積大小。在 gradle.properties 檔案中,將限制設定為 4、6 或 8 GB,如以下範例所示:

org.gradle.jvmargs=-Xmx6g

然後測試建構速度改善。如要判斷最佳堆積大小,最簡單的方法是將限制提高一點,然後測試充分建構速度。

如果您同時使用 JVM 平行垃圾收集器,則整行程式碼應如下所示:

org.gradle.jvmargs=-Xmx6g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g

您可以開啟 HeapDumpOnOutOfMemoryError 標記,藉此分析 JVM 記憶體錯誤。如此一來,若記憶體不足,JVM 就會產生記憶體快照資料。

使用非遞移 R 類別

使用非遞移 R 類別,以更快地建構具有多個模組的應用程式。這樣可以確保每個模組的 R 類別只包含其本身資源的參照,而不必提取其依附元件的參照,避免資源重複的情形,進而加快建構速度,同時享有避免編譯帶來的對應好處。這是 Android Gradle 外掛程式 8.0.0 以上版本的預設行為。

從 Android Studio Bumblebee 開始,新專案會預設啟用非遞移 R 類別。對於使用舊版 Android Studio 建立的專案,若要將其更新為使用非遞移 R 類別,請依序前往「Refactor」>「Migrate to Non-Transitive R Classes」

如要進一步瞭解應用程式資源和 R 類別,請參閱「應用程式資源總覽」一文。

使用非常數 R 類別

在應用程式與測試中使用非常數 R 類別欄位,可改善 Java 編譯的增量,並提供更準確的資源縮減。對程式庫而言,R 類別欄位一律為非常數,這是因為針對依附於程式庫的應用程式或測試封裝 APK 時,資源會加上編號。這是 Android Gradle 外掛程式 8.0.0 以上版本的預設行為。

停用 Jetifier 旗標

由於大多數專案都直接使用 AndroidX 程式庫,因此您可以移除 Jetifier 旗標以提升建構效能。如要移除 Jetifier 標記,請在 gradle.properties 檔案中設定 android.enableJetifier=false

版本分析器可以執行檢查,確認是否可安全移除該標記,確保您的專案能有更佳的建構效能,並從未維護的 Android 支援資料庫中遷移。如要進一步瞭解版本分析器,請參閱「藉助版本分析器排解版本效能問題」一文。

使用設定快取

設定快取可讓 Gradle 記錄建構工作圖表的資訊,並在後續建構作業中重複使用,Gradle 就不必重新設定整個建構作業。

如要啟用設定快取功能,請按照下列步驟操作:

  1. 確認所有專案外掛程式都相容。

    使用版本分析器檢查專案是否與設定快取相容。版本分析器會執行一系列測試版本,判斷是否要為專案啟用這項功能。請參閱問題 #13490,瞭解支援的外掛程式清單。

  2. 將下列程式碼新增至 gradle.properties 檔案。

      org.gradle.configuration-cache=true
      # Use this flag carefully, in case some of the plugins are not fully compatible.
      org.gradle.configuration-cache.problems=warn

啟用設定快取功能後,首次執行專案時,建構輸出內容會顯示 Calculating task graph as no configuration cache is available for tasks。後續執行期間,建構輸出內容會顯示 Reusing configuration cache

如要進一步瞭解設定快取功能,請參閱網誌文章「Configuration caching deep dive」(設定快取深入解析),以及 Gradle 說明文件「configuration cache」(設定快取)。

Gradle 8.1 和 Android Gradle 外掛程式 8.1 中引起的設定快取問題

設定快取在 Gradle 8.1 版中變得穩定,並且導入檔案 API 追蹤功能。File.exists()File.isDirectory()File.list() 等呼叫會由 Gradle 記錄以追蹤設定輸入檔案。

Android Gradle 外掛程式 (AGP) 8.1 會針對 Gradle 不應考慮為快取輸入內容的部分檔案使用這些 File API。與 Gradle 8.1 以上版本搭配使用時,這會觸發額外的快取撤銷作業,導致建構效能降低。在 AGP 8.1 中,以下內容將視為快取輸入:

輸入內容 Issue Tracker 修正位置
$GRADLE_USER_HOME/android/FakeDependency.jar 問題 #289232054 AGP 8.2
CMake 輸出 問題 #287676077 AGP 8.2
$GRADLE_USER_HOME/.android/analytics.settings 問題 #278767328 AGP 8.3

如果您使用這些 API 或使用這個 API 的外掛程式,建構時間可能會發生迴歸問題,因為某些使用這些 API 的建構邏輯可能會觸發額外的快取撤銷作業。請參閱「 建構設定輸入追蹤功能中的改善項目」一文,瞭解這些模式和修正建構邏輯的方法,或是暫時停用檔案 API 追蹤功能。