建立多個 APK

注意:自 2021 年 8 月起, 應用程式必須以 App Bundle 格式發布。如果您將應用程式發布到 Google Play 建立並上傳 Android App Bundle。時間 這樣一來,Google Play 就會自動為 每個使用者的裝置設定,因此他們只會下載程式碼和資源 需要執行您的應用程式如果您要 發布至不支援 AAB 格式的商店。在此情況下, 必須自行建構、簽署及管理每個 APK。

雖然建議您建立單一 APK 來支援所有目標裝置,但這麼做是比較理想的做法 造成 APK 過大, 支援多個 螢幕密度應用程式二進位檔 介面 (ABI)。縮減 APK 大小的方法之一是 多個 APK 包含特定螢幕密度或 ABI 的檔案。

Gradle 可以建立單獨的 APK,其中僅包含每個密度或 ABI 專屬的程式碼和資源。本頁說明如何設定版本以產生多個 APK。如果您需要建立不同版本的應用程式 並非以螢幕密度或 ABI 為依據,請改用建構變數

為多個 APK 設定版本

如要為多個 APK 設定版本,請將 splits 區塊新增至模組層級 build.gradle 檔案。在 splits 區塊,提供 density 區塊,用於指定 Gradle 產生方式 個別密度 APK 或指定 Gradle 所需方式的 abi 區塊 才能產生每個 ABI 的 APK您可以同時提供密度和 ABI 區塊,以及 建構系統會為每個密度和 ABI 組合建立 APK。

針對螢幕密度設定多個 APK

如要為不同螢幕密度建立個別 APK,請新增 density 在您的 splits 區塊。在 density 區塊中,提供 所需螢幕密度和相容螢幕大小的清單。僅使用 相容的螢幕大小 <compatible-screens> 元素。

下列 Gradle DSL 選項可為螢幕密度設定多個 APK:

enable 代表 Groovy,isEnable 適用於 Kotlin 指令碼
如果將此元素設為 true,Gradle 會產生多個 APK 自動調整資源配置預設值為 false
exclude
指定您不想讓 Gradle 使用的密度清單 (以半形逗號分隔) 產生獨立的 APK如果您符合以下情況,請使用exclude 想要產生適用於大多數密度的 APK 應用程式不支援的密度。
reset()

清除螢幕密度的預設清單。只有在與 include 元素可指定想要的密度 。

下列程式碼片段會將密度清單設定為 呼叫 reset() 即可ldpixxhdpi 以清除清單,然後使用 include

reset()                  // Clears the default list from all densities
                         // to no densities.
include "ldpi", "xxhdpi" // Specifies the two densities to generate APKs
                         // for.
include
指定您希望 Gradle 產生的密度清單 (以半形逗號分隔) APK。只有在與 reset() 合併使用以指定 指定確切的密度清單
compatibleScreens

指定相容螢幕大小 (以半形逗號分隔)。這個插入項目 相符的 <compatible-screens> 節點 。

你可以透過這項設定輕鬆管理兩個畫面 [密度和螢幕尺寸]build.gradle。 不過,使用 <compatible-screens>可以限制裝置類型 與應用程式搭配運作的方式如需支援 請參閱 螢幕相容性總覽

由於每個以螢幕密度為依據的 APK 都包含 有特定限制的 <compatible-screens> 標記 您必須瞭解 APK 支援的螢幕類型 (即使您發布多個 APK:部分新裝置與您的多個 APK 篩選器不符。因此 Gradle 一律會產生包含素材資源的額外通用 APK 適用於所有螢幕密度,且不包含 <compatible-screens> 標記。發布此內容 通用 APK 以及個別密度的 APK,為 APK 不符合 <compatible-screens> 標記。

下列範例會為每個 螢幕 密度,但 ldpixxhdpixxxhdpi。方法是使用 exclude 來移除 上述三個密度。

Groovy

android {
  ...
  splits {

    // Configures multiple APKs based on screen density.
    density {

      // Configures multiple APKs based on screen density.
      enable true

      // Specifies a list of screen densities you don't want Gradle to create multiple APKs for.
      exclude "ldpi", "xxhdpi", "xxxhdpi"

      // Specifies a list of compatible screen size settings for the manifest.
      compatibleScreens 'small', 'normal', 'large', 'xlarge'
    }
  }
}

Kotlin

android {
    ...
    splits {

        // Configures multiple APKs based on screen density.
        density {

            // Configures multiple APKs based on screen density.
            isEnable = true

            // Specifies a list of screen densities you don't want Gradle to create multiple APKs for.
            exclude("ldpi", "xxhdpi", "xxxhdpi")

            // Specifies a list of compatible screen size settings for the manifest.
            compatibleScreens("small", "normal", "large", "xlarge")
        }
    }
}

進一步瞭解如何自訂不同版本 針對特定螢幕類型和裝置的應用程式變化版本,請參閱 宣告受限制 螢幕支援

為 ABI 設定多個 APK

如要針對不同的 ABI 建立個別的 APK,請在 splits 區塊中新增 abi 區塊。在 abi 區塊中,提供一份清單 所需的 ABI。

下列 Gradle DSL 選項可為每個屬性設定多個 APK ABI:

enable 代表 Groovy,Kotlin 指令碼則為 isEnable
如果將此元素設為 true,Gradle 會產生多個 產生的 APK。預設值為 false
exclude
指定您希望 Gradle 存取的 ABI 清單 (以半形逗號分隔) 產生獨立的 APK如要產生金鑰,請使用「exclude」 適用於大多數 ABI 的 APK,但需要排除應用程式未提供的部分 ABI 聯絡。
reset()

清除 ABI 的預設清單。只有在與 include 元素:指定您要新增的 ABI。

下列程式碼片段會將 ABI 清單設為 x86 和 呼叫 reset() 以清除清單,為 x86_64 然後使用 include

reset()                 // Clears the default list from all ABIs to no ABIs.
include "x86", "x86_64" // Specifies the two ABIs we want to generate APKs for.
include
指定您希望 Gradle 產生 APK 的 ABI 清單 (以半形逗號分隔) 。只有在與 reset() 搭配使用以指定 ABI 的清單
universalApk 代表 Groovy,isUniversalApk 代表 Kotlin 指令碼

如果設為 true,Gradle 除了會產生通用 APK 每個 ABI 的 APK。通用 APK 包含所有 ABI 的程式碼和資源, 單一 APK。預設值為 false

請注意,這個選項 可在 splits.abi 區塊中取得建立多個 APK 時 Gradle 一律會產生通用 APK 包含所有螢幕密度適用的程式碼和資源。

下列範例會為每個 ABI 產生單獨的 APK:x86x86_64。方法是使用 reset() 開頭是空白 ABI 清單,後面接著 include 每個取得 APK 的 ABI 清單。

Groovy

android {
  ...
  splits {

    // Configures multiple APKs based on ABI.
    abi {

      // Enables building multiple APKs per ABI.
      enable true

      // By default all ABIs are included, so use reset() and include to specify that you only
      // want APKs for x86 and x86_64.

      // Resets the list of ABIs for Gradle to create APKs for to none.
      reset()

      // Specifies a list of ABIs for Gradle to create APKs for.
      include "x86", "x86_64"

      // Specifies that you don't want to also generate a universal APK that includes all ABIs.
      universalApk false
    }
  }
}

Kotlin

android {
  ...
  splits {

    // Configures multiple APKs based on ABI.
    abi {

      // Enables building multiple APKs per ABI.
      isEnable = true

      // By default all ABIs are included, so use reset() and include to specify that you only
      // want APKs for x86 and x86_64.

      // Resets the list of ABIs for Gradle to create APKs for to none.
      reset()

      // Specifies a list of ABIs for Gradle to create APKs for.
      include("x86", "x86_64")

      // Specifies that you don't want to also generate a universal APK that includes all ABIs.
      isUniversalApk = false
    }
  }
}

如需支援的 ABI 清單,請參閱 支援 ABI

不含原生/C++ 程式碼的專案

如果專案沒有原生/C++ 程式碼,「Build Variants」面板會有兩個 資料欄:ModuleActive Build 子類,如圖 1 所示。

「BuildVariant」面板
圖 1.「Build Variants」面板會為沒有兩個資料欄的專案提供兩個資料欄 原生/C++ 程式碼

「Active Build Variant」值: 模組會決定目前部署並在編輯器中顯示的建構變數。 如要切換變化版本,請按一下模組的「Active Build Variant」儲存格 然後從清單欄位中選擇所需的變化版本

含有原生/C++ 程式碼的專案

如果專案含有原生/C++ 程式碼,「Build Variants」面板會顯示三個 資料欄:ModuleActive Build 變化版本有效 ABI,如圖 2 所示。

圖 2. 「Build Variants」面板會新增「Active ABI」欄: 並在專案中使用原生/C++ 程式碼

模組的「Active Build Variant」值 決定已部署且顯示在編輯器中的建構變數。 以原生模組來說,「Active ABI」值會決定編輯器的 ABI 但不會影響部署的內容

如要變更建構類型或 ABI,請執行下列操作:

  1. 按一下「Active Build Variant」的儲存格 或「Active ABI」資料欄。
  2. 從清單中選擇需要的變化版本或 ABI ] 欄位。系統會自動執行新的同步作業。

變更應用程式或資料庫模組的任一資料欄後,該變更會套用至所有應用程式 參照資料列。

設定版本管理

根據預設,Gradle 產生多個 APK 時,每個 APK 都相同 如模組層級所指定 build.gradlebuild.gradle.kts 檔案。由於 Google Play 商店不允許同一個應用程式使用多個 APK, 相同的版本資訊,但您必須確保每個 APK 都有專屬的 請先versionCode,再上傳至 Play 商店。

您可以將模組層級的 build.gradle 檔案覆寫每個 APK 的 versionCode。建立對應關係 為您設定的每個 ABI 和密度指派專屬數值 多個 APK,您可以使用下列值覆寫輸出版本代碼: 結合 defaultConfig 中定義的版本代碼或 productFlavors 個區塊,當中含有指派給 例如密度或 ABI

在以下範例中,x86 ABI 的 APK 取得 2004 和 x86_64 ABI 的 versionCode 獲得 3004 的 versionCode

一次指派 1000 等遞增版本代碼, 稍後需要更新應用程式時,再指派不重複的版本代碼。適用對象 舉例來說,如果 defaultConfig.versionCode 疊代為 後續更新,Gradle 會指派 2005 的 versionCodex86 APK 和 x86_64 APK。

提示:如果您的版本包含通用 APK,請為該版本指派 versionCode (低於任何其他 APK)。 由於 Google Play 商店會安裝與目標裝置相容的應用程式版本,並具有最高的 versionCode,因此將較低的 versionCode 指派給通用 APK 可確保 Google Play 商店會先嘗試安裝其中一個 APK,然後才退回使用通用 APK。以下程式碼範例 避免覆寫通用 APK 的 預設值為 versionCode

Groovy

android {
  ...
  defaultConfig {
    ...
    versionCode 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
ext.abiCodes = ['armeabi-v7a':1, x86:2, x86_64:3]

// For per-density APKs, create a similar map:
// ext.densityCodes = ['mdpi': 1, 'hdpi': 2, 'xhdpi': 3]

import com.android.build.OutputFile

// For each APK output variant, override versionCode with a combination of
// ext.abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
android.applicationVariants.all { variant ->

  // Assigns a different version code for each output APK
  // other than the universal APK.
  variant.outputs.each { output ->

    // Stores the value of ext.abiCodes that is associated with the ABI for this variant.
    def baseAbiVersionCode =
            // Determines the ABI for this variant and returns the mapped value.
            project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))

    // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
    // the following code doesn't override the version code for universal APKs.
    // However, because you want universal APKs to have the lowest version code,
    // this outcome is desirable.
    if (baseAbiVersionCode != null) {

      // Assigns the new version code to versionCodeOverride, which changes the
      // version code for only the output APK, not for the variant itself. Skipping
      // this step causes Gradle to use the value of variant.versionCode for the APK.
      output.versionCodeOverride =
              baseAbiVersionCode * 1000 + variant.versionCode
    }
  }
}

Kotlin

android {
  ...
  defaultConfig {
    ...
    versionCode = 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
val abiCodes = mapOf("armeabi-v7a" to 1, "x86" to 2, "x86_64" to 3)

// For per-density APKs, create a similar map:
// val densityCodes = mapOf("mdpi" to 1, "hdpi" to 2, "xhdpi" to 3)

import com.android.build.api.variant.FilterConfiguration.FilterType.*

// For each APK output variant, override versionCode with a combination of
// abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
androidComponents {
    onVariants { variant ->

        // Assigns a different version code for each output APK
        // other than the universal APK.
        variant.outputs.forEach { output ->
            val name = output.filters.find { it.filterType == ABI }?.identifier

            // Stores the value of abiCodes that is associated with the ABI for this variant.
            val baseAbiCode = abiCodes[name]
            // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
            // the following code doesn't override the version code for universal APKs.
            // However, because you want universal APKs to have the lowest version code,
            // this outcome is desirable.
            if (baseAbiCode != null) {
                // Assigns the new version code to output.versionCode, which changes the version code
                // for only the output APK, not for the variant itself.
                output.versionCode.set(baseAbiCode * 1000 + (output.versionCode.get() ?: 0))
            }
        }
    }
}

如要更多替代版本代碼配置的範例,請參閱「指派版本代碼」。

建立多個 APK

設定模組層級 build.gradlebuild.gradle.kts 個檔案來建立多個 APK,按一下 建構 >建構 APK,用來建構目前適用於 在「Project」窗格中選取模組。Gradle 會建立 APK 針對專案 build/outputs/apk/ 中的每個像素密度或 ABI 進行自訂 目錄。

Gradle 會針對您設定的多個 APK 的每個密度或 ABI 建構一個 APK。如果您同時為密度和 ABI 啟用多個 APK,Gradle 會建立 APK 。

例如,下列 build.gradle 程式碼片段可用來為 mdpihdpi密度,以及 x86x86_64 ABI:

Groovy

...
  splits {
    density {
      enable true
      reset()
      include "mdpi", "hdpi"
    }
    abi {
      enable true
      reset()
      include "x86", "x86_64"
    }
  }

Kotlin

...
  splits {
    density {
      isEnable = true
      reset()
      include("mdpi", "hdpi")
    }
    abi {
      isEnable = true
      reset()
      include("x86", "x86_64")
    }
  }

範例設定中的輸出內容含有以下 4 個 APK:

  • app-hdpiX86-release.apk:包含以下項目的程式碼和資源: hdpi 密度和 x86 ABI。
  • app-hdpiX86_64-release.apk:包含以下項目的程式碼和資源: hdpi 密度和 x86_64 ABI。
  • app-mdpiX86-release.apk:包含以下項目的程式碼和資源: mdpi 密度和 x86 ABI。
  • app-mdpiX86_64-release.apk:包含以下項目的程式碼和資源: mdpi 密度和 x86_64 ABI。

根據螢幕密度建構多個 APK 時,除了個別密度 APK 以外,Gradle 一律會產生通用 APK,其中包含所有密度適用的程式碼和資源。

根據 ABI,Gradle 只會產生 APK,其中包含所有使用者的程式碼和資源 如果您在universalApk true build.gradle 檔案中有 splits.abi 個區塊 (適用於 Groovy) 或 isUniversalApk = true build.gradle.kts 檔案中的 splits.abi 個區塊 (適用於 Kotlin 指令碼)。

APK 檔案名稱格式

建構多個 APK 時,Gradle 會使用下列指令產生 APK 檔案名稱 配置:

modulename-screendensityABI-buildvariant.apk

配置元件如下:

modulename
指定正在建構的模組名稱。
screendensity
如果已啟用多個螢幕密度的 APK,請指定螢幕 APK 的密度,例如 mdpi
ABI

如果啟用了多個 ABI 的 APK,請指定 APK 的 ABI,例如 使用 x86

如果同時啟用多個適用於螢幕密度和 ABI 的 APK, Gradle 會將密度名稱與 ABI 名稱串連在一起,例如 mdpiX86。如果為個別 ABI 啟用 universalApk APK,Gradle 會使用 universal 做為通用 APK 的 ABI 部分 即可。

buildvariant
指定目前建構的建構變數,例如 debug

舉例來說,在建構 mdpi 螢幕密度 APK myApp 偵錯版本,APK 檔案名稱為 myApp-mdpi-debug.apk。發布內容 設定為同時建立多個 APK 的 myApp 版本 mdpi 螢幕密度和 x86 ABI 的 APK 檔案名稱為 myApp-mdpiX86-release.apk