複数の APK をビルドする

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

すべての対象デバイスをサポートするには、可能な限り単一の APK をビルドする方が得策ですが、複数の 画面密度アプリケーション バイナリ インターフェース(ABI)をサポートするファイルが原因で、APK のサイズが非常に大きくなる可能性があります。APK のサイズを小さくする方法の一つは、特定の画面密度または 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

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

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

画面密度に基づく各 APK には <compatible-screens> タグが含まれており、APK がサポートする画面タイプについて特定の制限があります。複数の 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 のリストを指定します。

ABI ごとに複数の APK を設定するには、以下の Gradle DSL オプションを使用します。

enable(Groovy の場合)または isEnable(Kotlin スクリプトの場合)
この要素を true に設定すると、定義した ABI に基づいて複数の APK が Gradle によって生成されます。デフォルト値は false です。
exclude
Gradle で APK を個別に生成しない ABI のカンマ区切りリストを指定します。exclude は、ほとんどの ABI 用の APK を生成するものの、アプリでサポートしていない一部の ABI を除外する必要がある場合に使用します。
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() と組み合わせて使用します。
universalApk(Groovy の場合)または isUniversalApk(Kotlin スクリプトの場合)

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 には影響しません。

ビルドタイプまたは 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 は x86 APK に 2005 の versionCode を、x86_64 APK に 3,005 という versionCode を割り当てます。

ヒント: ビルドにユニバーサル 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 のスニペットを使用すると、mdpihdpi の密度に加え、x86x86_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 の場合)または build.gradle.kts ファイルの splits.abi ブロック(Kotlin スクリプトの場合)で 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 です。