複数の APK をビルドする

注意: 2021 年 8 月以降、新しいアプリはすべて App Bundle として公開する必要があります。アプリを Google Play に公開する場合は、Android App Bundle を作成してアップロードします。そうすると、Google Play は各ユーザーのデバイス設定に合わせて最適化された APK を自動的に生成して配信するため、ユーザーはアプリの実行に必要なコードとリソースのみをダウンロードします。複数の APK を公開すると、AAB 形式をサポートしていないストアに公開する場合に便利です。その場合は、各 APK をデベロッパー自身でビルド、署名、管理する必要があります。

すべての対象デバイスをサポートするには、可能な限り単一の APK をビルドすることをおすすめしますが、複数の 画面密度アプリケーション バイナリ インターフェース(ABI)をサポートするファイルが原因で、APK のサイズが非常に大きくなる可能性があります。APK のサイズを縮小する 1 つの方法は、特定の画面密度や ABI 用のファイルを含む複数の APK を作成することです。

Gradle では、各画面密度や ABI に固有のコードとリソースのみを含む APK を個別に作成できます。このページでは、複数の APK を生成するようにビルドを構成する方法について説明します。画面密度や ABI によらずに異なるバージョンのアプリを作成する必要がある場合は、代わりにビルド バリアントを使用してください。

複数の APK 用のビルドを構成する

複数の APK 用のビルドを構成するには、モジュール レベルの build.gradle ファイルに splits ブロックを追加します。splits ブロック内で、Gradle で密度ごとの APK を生成する方法を指定する density ブロックか、Gradle で ABI ごとの APK を生成する方法を指定する abi ブロックを指定します。密度ブロックと ABI ブロックの両方を提供でき、ビルドシステムが密度と ABI の組み合わせごとに APK を作成します。

画面密度用の複数の APK の構成

画面密度ごとに異なる APK を作成するには、splits ブロック内に density ブロックを追加します。density ブロックで、目的の画面密度と互換性のある画面サイズのリストを指定します。各 APK のマニフェストで特定の <compatible-screens> 要素が必要な場合にのみ、互換性のある画面サイズのリストを使用してください。

画面密度用の複数の APK を設定するには、次の Gradle DSL オプションを使用します。

enable(Groovy の場合)、isEnable(Kotlin スクリプトの場合)
この要素を true に設定すると、Gradle は定義した画面密度に基づいて複数の APK を生成します。デフォルト値は false です。
exclude
Gradle で個別の APK を生成しない密度のカンマ区切りリストを指定します。ほとんどの密度に対応した APK を生成するものの、アプリがサポートしていない密度を除外する必要がある場合は、exclude を使用します。
reset()

画面密度のデフォルト リストを消去します。include 要素と組み合わせて、追加する画面密度を指定する場合にのみ使用します。

次のスニペットでは、reset() を呼び出してリストをクリアしてから include を使用することで、密度のリストを ldpixxhdpi のみに設定しています。

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

互換性のある画面サイズのカンマ区切りリストを指定します。これにより、各 APK のマニフェストに、一致する <compatible-screens> ノードが挿入されます。

この設定により、画面密度と画面サイズの両方を同じ build.gradle セクションで簡単に管理できるようになります。ただし、<compatible-screens> を使用すると、アプリが操作できるデバイスのタイプが制限されることがあります。さまざまな画面サイズをサポートする別の方法については、画面の互換性の概要をご覧ください。

画面密度に基づく各 APK には、APK がサポートする画面タイプに関する特定の制限がある <compatible-screens> タグが含まれるため(複数の APK を公開した場合であっても)、一部の新しいデバイスは複数の APK のフィルタと一致しないことがあります。そのため、Gradle は常に、すべての画面密度用のアセットを含み、<compatible-screens> タグを含まない追加のユニバーサル APK を生成します。このユニバーサル APK を密度ごとの APK とともに公開し、<compatible-screens> タグのある APK と一致しないデバイス用のフォールバックを提供します。

次の例では、画面密度ldpixxhdpixxxhdpi を除く)ごとに個別の APK を生成します。これを行うには、exclude を使用して、すべての密度のデフォルト リストからこれら 3 つの密度を削除します。

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 オプションを使用して、ABI ごとに複数の APK を構成します。

Groovy の場合は enable、Kotlin スクリプトの場合は isEnable です。
この要素を true に設定すると、Gradle は定義する ABI に基づいて複数の APK を生成します。デフォルト値は false です。
exclude
Gradle で個別の APK を生成しない ABI のカンマ区切りリストを指定します。ほとんどの ABI 用の APK を生成するものの、アプリでサポートしない ABI をいくつか除外する必要がある場合は、exclude を使用します。
reset()

ABI のデフォルト リストを消去します。include 要素と組み合わせて、追加する ABI を指定する場合にのみ使用します。

次のスニペットでは、reset() を呼び出してリストをクリアしてから include を使用することで、ABI のリストを x86x86_64 のみに設定しています。

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 のカンマ区切りリストを指定します。ABI の正確なリストを指定する場合にのみ、reset() と組み合わせて使用します。
Groovy の場合は universalApk、Kotlin スクリプトの場合は isUniversalApk です。

true の場合、Gradle は ABI ごとの APK に加えてユニバーサル APK も生成します。ユニバーサル APK は、すべての ABI のコードとリソースを単一の APK に含めます。デフォルト値は false です。

このオプションは、splits.abi ブロックでのみ使用できます。画面密度に基づいて複数の APK をビルドする場合、Gradle は常にすべての画面密度用のコードとリソースを含むユニバーサル APK を生成します。

次の例では、各 ABI(x86x86_64)用の APK を個別に生成しています。これを行うには、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] パネルには、図 1 に示すように、[Module] と [Active Build Variant] という 2 つの列が表示されます。

[Build Variants] パネル
図 1. ネイティブ/C++ コードを使用しないプロジェクトの場合、[Build Variants] パネルには 2 つの列が表示されます。

モジュールの [Active Build Variant] 値により、デプロイされてエディタで表示されるビルド バリアントが決定されます。バリアントを切り替えるには、モジュールの [Active Build Variant] セルをクリックし、リスト フィールドから目的のバリアントを選択します。

ネイティブ / C++ コードを含むプロジェクト

ネイティブ/C++ コードを含むプロジェクトの場合、[Build Variants] パネルには、図 2 に示すように、[Module]、[Active Build Variant]、[Active ABI] の 3 つの列が表示されます。

図 2. ネイティブ/C++ コードを含むプロジェクトの場合、[Build Variants] パネルに [Active ABI] 列が追加されます。

モジュールの [Active Build Variant] 値により、デプロイされ、エディタで表示されるビルド バリアントが決定されます。ネイティブ モジュールの場合は、[Active ABI] 値によってエディタが使用する ABI が決定されますが、デプロイされるものには影響しません。

ビルドタイプまたは ABI を変更するには:

  1. [Active Build Variant] または [Active ABI] 列のセルをクリックします。
  2. リスト フィールドから目的のバリアントまたは ABI を選択します。新しい同期が自動的に実行されます。

アプリまたはライブラリ モジュールのいずれかの列を変更すると、すべての依存行に変更が適用されます。

バージョン管理の構成

Gradle が複数の APK を生成する場合、デフォルトでは、モジュール レベルの build.gradle または build.gradle.kts ファイルで指定されているバージョン情報が、各 APK に同じものになります。Google Play ストアでは、同じアプリに対して、すべて同じバージョン情報を持つ複数の APK は認められていないため、Play ストアにアップロードする前に、各 APK が一意の versionCode を持つことを確認する必要があります。

モジュール レベルの build.gradle ファイルを設定すると、各 APK の versionCode をオーバーライドできます。複数の APK を構成する ABI と密度ごとに一意の数値を割り当てるマッピングを作成することで、出力バージョン コードを、defaultConfig ブロックまたは productFlavors ブロック内で定義されたバージョン コードと、密度または ABI に割り当てられた数値を組み合わせた値でオーバーライドできます。

次の例では、x86 ABI の APK の versionCode は 2004 で、x86_64 ABI の versionCode は 3004 です。

バージョン コードを 1,000 など大きな単位で割り当てると、アプリをアップデートする必要が生じた場合に後で一意のバージョン コードを割り当てることができます。たとえば、後続のアップデートで defaultConfig.versionCode が 5 にイテレーションされた場合、Gradle は versionCodex86 APK に 2005、x86_64 APK に 3005 を割り当てます。

ヒント: ビルドにユニバーサル APK が含まれている場合は、他の APK よりも小さい versionCode を割り当てます。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 をビルドする

複数の APK をビルドするようにモジュール レベルの build.gradle ファイルまたは build.gradle.kts ファイルを設定したら、[Build] > [Build APK] をクリックして、[Project] ペインで現在選択されているモジュールの APK をすべてビルドします。Gradle は、プロジェクトの build/outputs/apk/ ディレクトリに各密度または ABI 用の APK を作成します。

Gradle は、複数の APK を構成した画面密度または ABI ごとに APK をビルドします。 密度と ABI の両方に対して複数の APK を有効にすると、Gradle は密度と ABI の組み合わせごとに APK を作成します。

たとえば、次の build.gradle スニペットを使用すると、mdpi 密度と hdpi 密度、および x86 ABI と x86_64 ABI 用に複数の APK をビルドできます。

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 をビルドする場合、Gradle では常に、画面密度ごとの APK に加え、すべての画面密度用のコードとリソースを含むユニバーサル APK が生成されます。

ABI に基づいて複数の APK をビルドする場合、build.gradle ファイルの splits.abi ブロック(Groovy の場合)または isUniversalApk = true(Kotlin スクリプトの場合)の build.gradle.kts ファイルの splits.abi ブロックで isUniversalApk = true を指定した場合、Gradle は、すべての ABI のコードとリソースを含む APK のみを生成します。 universalApk true

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 ごとの APK で universalApk が有効になっている場合、Gradle はユニバーサル APK ファイル名の ABI 部分として universal を使用します。

buildvariant
ビルドするビルド バリアントを指定します(debug など)。

たとえば、myApp のデバッグ バージョン用に mdpi 画面密度 APK をビルドする場合、APK のファイル名は myApp-mdpi-debug.apk になります。mdpi 画面密度と x86 ABI の両方用に複数の APK をビルドするよう構成されている myApp のリリース バージョンの APK ファイル名は myApp-mdpiX86-release.apk です。