1. 程式人生 > >優化App的構建速度

優化App的構建速度

Google 原文連結https://developer.android.com/studio/build/optimize-your-build.html

長構建時間會減慢您的開發過程,因此本頁面將介紹一些可以幫助您解決構建速度瓶頸的技巧。

改進您的構建速度的一般過程如下所示:

  1. 採取一些可以立即為大多數 Android Studio 專案帶來好處的措施,優化您的構建配置
  2. 分析您的構建,確定並診斷一些對您的專案或工作站來說比較棘手的瓶頸。

在開發您的應用時,如果可以,您應部署到正在執行 Android 7.0(API 級別 24)或更高版本的裝置。較新版本的 Android 平臺實現更好的機制來向您的應用推送更新,例如 Android 執行時 (ART) 以及對多個 DEX 檔案的原生支援。

:在您的首個乾淨構建後,您可能會注意到後續構建(乾淨和增量)的執行速度明顯加快(即使沒有使用本頁介紹的任何優化措施,也是如此)。這是因為 Gradle 後臺程式有一個提升效能的“預熱”期 - 與其他 JVM 程序類似。

優化您的構建配置


按照下面的提示操作,提升您的 Android Studio 專案的構建速度。

保持工具處於最新狀態

Android 工具幾乎在每一次更新中都會獲得構建優化和新功能,本頁介紹的一些提示假設您正在使用最新版本。要充分利用最新優化,請保持以下工具處於最新狀態:

為開發建立構建變體

準備釋出應用時需要的許多配置在開發應用時都不需要。啟用不必要的構建程序會減慢您的增量構建和乾淨構建速度,因此,在開發您的應用期間,配置一個構建變體,使之僅包含您需要的構建配置。下面的示例將為您的釋出版本配置建立一個“開發”風味和一個“生產”風味:

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.     }   } }

如果您的構建配置已使用產品風味建立不同版本的應用,您可以使用風味維度,將“開發”和“生產”配置與這些風味組合。例如,如果您已配置“演示”和“完整”風味,您可以使用下面的示例配置建立組合風味,例如“devDemo”和“prodFull”:

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"
      ...
    }
  }
}

避免編譯不必要的資源

避免編譯和打包您沒有測試的資源(例如其他語言本地化和螢幕密度資源)。為此,您可以僅為“開發”風味指定一個語言資源和螢幕密度,如下面的示例中所示:

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

為您的除錯構建停用 Crashlytics

如果您不需要執行 Crashlytics 報告,請按以下步驟操作來停用外掛,以便加快您的除錯構建的速度:

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

您也需要更改在應用中為 Fabric 初始化支援的方式,在執行時為除錯構建停用 Crashlytics 套件,如下所示:

// 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);

如果您想要將 Crashlytics 與除錯構建結合使用,仍可以通過以下方式來加快增量構建的速度:在每個構建期間阻止 Crashlytics 使用它自己的獨特構建 ID 更新應用資源。要阻止 Crashlytics 不斷更新其構建 ID,請將以下程式碼新增到您的 build.gradle 檔案中:

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

如需瞭解有關在使用 Crashlytics 時優化您的構建的詳細資訊,請閱讀官方文件

將靜態構建配置值與除錯構建結合使用

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

例如,在您每次想要執行更改時,使用動態版本程式碼、版本名稱、資源或任何其他可以更改 manifest 檔案的構建邏輯都需要一個完整的 APK 構建 - 即使實際更改僅需要一個熱交換,也是如此。如果您的構建配置需要此類動態屬性,那麼將其隔離到您的釋出構建變體中並讓值對您的除錯構建保持靜態,如下面的 build.gradle 檔案所示。

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 APK build and reinstallation because the AndroidManifest.xml
        // must be updated (which is not supported by Instant Run).
        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 APK, 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;
        }
    }
}

使用靜態依賴項版本

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

啟用離線模式

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

要在使用 Android Studio 構建時離線使用 Gradle,請執行以下操作:

  1. 點選 File > Settings(在 Mac 上,點選 Android Studio > Preferences),開啟 Preferences 視窗。
  2. 在左側窗格中,點選 Build, Execution, Deployment > Gradle
  3. 勾選 Offline work 複選框。
  4. 點選 Apply 或 OK

如果您正在從命令列構建,請傳遞 --offline 選項。

啟用按需配置

為了讓 Gradle 準確瞭解如何構建您的應用,構建系統會在每個構建前在專案中配置所有模組以及這些模組的依賴項(即使您正在構建和測試一個模組,也是如此)。這會減慢大型多模組專案的構建程序。要指示 Gradle 僅配置您想要構建的模組,請按以下步驟操作,啟用按需配置

  1. 點選 File > Settings(在 Mac 上,點選 Android Studio > Preferences),開啟 Preferences 視窗。
  2. 在左側窗格中,點選 Build, Execution, Deployment > Compiler
  3. 勾選 Configure on demand 複選框。
  4. 點選 Apply 或 OK

建立庫模組

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

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

在您建立構建分析後,如果分析顯示構建時間中相當大的一部分用在了“配置專案”階段,請檢查 build.gradle 指令碼並查詢您可以新增到自定義 Gradle 任務中的程式碼。將某個構建邏輯移動到任務中後,它僅會在需要時執行,可以為後續構建快取結果,並且該構建邏輯將有資格並行執行(如果您啟用並行專案執行)。要了解詳情,請閱讀官方 Gradle 文件

提示:如果您的構建包含大量自定義任務,您可能還需要通過建立自定義任務類來整理 build.gradle 檔案。將您的類新增到 project-root/buildSrc/src/main/groovy/ 目錄中,Gradle 會自動將其新增到專案中所有 build.gradle 檔案的類路徑中。

配置 dexOptions 和啟用庫預 dexing

Android 外掛提供了 dexOptions 程式碼塊,因此,您可以配置以下 DEX 構建屬性,這樣可能會加快構建速度:

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

例如:

android {
  ...
  dexOptions {
    preDexLibraries true
    maxProcessCount 8
    // Instead of setting the heap size for the DEX process, increase Gradle's
    // heap size to enable dex-in-process. To learm more, read the next section.
    // javaMaxHeapSize "2048m"
  }
}

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

:如果 Gradle 後臺程序已在執行,您需要先停止此程序,然後使用新設定進行初始化。您可以選擇 View > Tool Windows > Terminal 並輸入 gradlew --stop 命令來終止 Gradle 後臺程式。

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

Dex-in-process 在構建程序內而不是單獨的 VM 程序中執行 DEX 編譯器 - 這會同時加快增量構建和乾淨構建的速度。預設情況下,使用 Android Studio 2.1 及更高版本建立的新專案將向構建程序分配足夠的記憶體來啟用此功能。如果您並非使用 Android Studio 2.1 或更高專案建立專案,則需要將 Gradle 後臺程式的最大堆大小至少設定為 1536 MB。以下示例會在專案的 gradle.properties 檔案中將 Gradle 的堆大小設定為 2048 MB:

org.gradle.jvmargs = -Xmx2048m

一些較大的專案可能會從更大的 Gradle 堆大小中受益。不過,如果您正在使用記憶體較小的機器,可能需要配置 IDE,使其使用較少的記憶體。要了解您分配給 IDE 和 Gradle 的資源量如何影響構建效能,請轉到有關分析您的構建的部分。

:如果 Gradle 後臺程序已在執行,您需要先停止此程序,然後使用新設定進行初始化。您可以選擇 View > Tool Windows > Terminal 並輸入 gradlew --stop 命令來終止 Gradle 後臺程式。

如果您在模組的 build.gradle 檔案(控制著 DEX 編譯器的堆大小)中為 android.dexOptions.javaMaxHeapSize 定義值,則需要將 Gradle 的堆大小設定為比您為 javaMaxHeapSize 屬性設定的值大 512 MB 並且至少為 1536 MB。例如,如果您將 javaMaxHeapSize 設為 1280 MB,則必須將 org.gradle.jvmargs 至少設定為 1792 MB (1280 + 512),不過,堆大小越大越好。您不需要為 javaMaxHeapSize 指定值來啟用 dex-in-process。如果您將 javaMaxHeapSize 從構建配置中排除,只需確保將 Gradle 的堆大小設定為 1536 MB 或更高。

將影象轉換成 WebP

WebP 是一種既可以提供有失真壓縮(像 JPEG 一樣)也可以提供透明度(像 PNG 一樣)的圖片檔案格式,不過與 JPEG 或 PNG 相比,這種格式可以提供更好的壓縮。降低圖片檔案大小可以加快構建的速度(無需執行構建時壓縮),尤其是在您的應用使用大量影象資源時,更是如此。不過,在對 WebP 影象進行解壓縮時,您可能會注意到裝置的 CPU 使用率有小幅上升。使用 Android Studio 時,您可以輕鬆地將影象轉換成 WebP

停用 PNG 處理

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

android {
  ...
  aaptOptions {
    cruncherEnabled false
  }
}

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

啟用 Instant Run

Instant Run 可以在不構建新 APK 的情況下(某些情況下,甚至不需要重啟當前 Activity)推送特定程式碼和資源更改,從而顯著縮短更新您的應用所需的時間。在對您的應用進行更改後,點選 Apply Changes  以使用 Instant Run,此功能會在您執行以下操作時預設啟用:

  • 使用除錯構建變體構建您的應用。
  • 使用 Android Plugin for Gradle 2.3.0 或更高版本。
  • 在應用的模組級 build.gradle 檔案中將 minSdkVersion 設定為 15 或更高。
  • 點選 Run ,將您的應用部署到執行 Android 5.0(API 級別 21)及更高版本的裝置上。

如果滿足這些要求後您在工具欄中沒有看到 Apply Changes  按鈕,請按以下步驟操作,確保未在 IDE 設定中停用 Instant Run:

  1. 開啟 Settings 或者 Preferences 對話方塊。
  2. 導航至 Build, Execution, Deployment > Instant Run
  3. 確保勾選 Enable Instant Run

啟用構建快取

構建快取可以儲存 Android Plugin for Gradle 在構建您的專案時生成的特定輸出(例如未打包的 AAR 和 pre-dexed 遠端依賴項)。使用快取時,您的乾淨構建將顯著加快,因為構建系統在後續構建期間可以直接重用這些快取檔案,而不用重新建立它們。

使用 Android 外掛 2.3.0 及更高版本的新專案在預設情況下會啟用構建快取(除非您明確停用構建快取)。要了解詳情,請閱讀使用構建快取加快乾淨構建的速度

停用註解處理器

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

分析您的構建


較大的專案或者實現許多自定義構建邏輯的專案可能要求您深入檢視構建程序才能找到瓶頸。為此,您可以分析 Gradle 執行構建生命週期的每個階段和每個構建任務所需的時間。例如,如果您的構建分析顯示 Gradle 在配置專案時花費了過多的時間,可能表明您需要將自定義構建邏輯移出配置階段。此外,如果 mergeDevDebugResources 任務佔用了大量構建時間,則表明您還需要將影象轉換成 WebP 或者停用 PNG 處理

使用分析來加快構建速度一般涉及在啟用分析的情況下執行構建,對構建配置進行一些調整,以及進行更多分析來觀察更改的結果。

要生成和檢視構建分析,請執行以下步驟:

  1. 在 Android Studio 中開啟您的專案後,選擇 View > Tool Windows > Terminal 以在專案的根目錄下開啟命令列。
  2. 輸入以下命令來執行乾淨構建。在分析構建時,您應當在分析的每個構建之間執行乾淨構建,因為 Gradle 會在任務(例如原始碼)的輸入未發生變化時跳過任務。因此,沒有輸入更改的第二個構建總會以更快的速度執行,因為任務不會重複執行。在不同構建之間執行 clean 任務可以確保您分析完整的