アプリを Google Play に公開する場合は、Android App Bundle を作成してアップロードしてください。そうすることで、各ユーザーのデバイス設定に合わせて最適化された APK が Google Play によって自動的に生成、配信されるため、ユーザーはアプリの実行に必要なコードとリソースをダウンロードするだけで済みます。複数の 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
-
この要素を
true
に設定すると、Gradle により、定義した画面密度に基づいて複数の APK が生成されます。デフォルト値はfalse
です。 -
exclude
-
Gradle での APK 生成の対象外の画面密度をカンマ区切りのリストで指定します。ほとんどの画面密度用の APK を生成するものの、アプリでサポートしない一部の画面密度を除外する必要がある場合は、
exclude
を使用します。 -
reset()
-
画面密度のデフォルトのリストをクリアします。
include
要素と組み合わせて、追加したい画面密度を指定する場合にのみ使用します。次のスニペットでは、reset()
を呼び出してリストをクリアしてからinclude
を使用することで、ldpi
とxxhdpi
にのみ画面密度のリストを設定しています。reset() // Clears the default list from all densities to no densities. include "ldpi", "xxhdpi" // Specifies the two densities we want to generate APKs for.
-
include
-
Gradle での APK 生成の対象の画面密度をカンマ区切りのリストで指定します。画面密度の正確なリストを指定するには、
reset()
と組み合わせて使用する必要があります。 -
compatibleScreens
-
互換性のある画面サイズをカンマ区切りのリストで指定します。これにより、一致する
<compatible-screens>
ノードが各 APK のマニフェストに追加されます。この設定を使用すると、画面密度と画面サイズの両方を同じbuild.gradle
セクションで簡単に管理できます。ただし、<compatible-screens>
を使用すると、アプリが動作するデバイスのタイプが制限されることがあります。さまざまな画面サイズをサポートする別の方法については、複数の画面のサポートをご覧ください。
画面密度に基づく各 APK には、APK がサポートする画面タイプに関する特定の制限付きの <compatible-screens>
タグが含まれるため、複数の APK を公開しても、新しいデバイスが複数の APK のフィルタと一致しないことがあります。そのため、Gradle では常に、あらゆる画面密度に対応するアセットを含み、<compatible-screens>
タグを含まない追加のユニバーサル APK が生成されます。このユニバーサル APK を画面密度ごとの APK とともに公開し、<compatible-screens>
タグを含む APK と一致しないデバイスにはフォールバックを提供する必要があります。
次の例では、サポート対象画面の範囲に記載されている各画面密度(ldpi
、xxhdpi
、xxxhdpi
を除く)用の 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 Gradle should not 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 Gradle should not 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
- この要素を
true
に設定した場合、定義した ABI に基づいて Gradle で複数の APK が生成されます。デフォルト値はfalse
です。 -
exclude
-
Gradle での APK 生成の対象外の ABI をカンマ区切りのリストで指定します。ほとんどの ABI 用の APK を生成するものの、アプリでサポートしない一部の ABI を除外する必要がある場合は、
exclude
を使用します。 -
reset()
-
ABI のデフォルトのリストをクリアします。
include
要素と組み合わせて、追加したい ABI を指定する場合にのみ使用します。次のスニペットでは、reset()
を呼び出してリストをクリアしてからinclude
を使用することで、x86
とx86_64
にのみ ABI のリストを設定しています。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
-
true
を指定すると、ABI ごとの APK に加えてユニバーサル APK が Gradle で生成されます。ユニバーサル APK では、1 つの APK にすべての ABI のコードとリソースが含まれます。 デフォルト値はfalse
です。このオプションを指定できるのはsplits.abi
ブロックのみです。画面密度に基づいて複数の APK をビルドする場合、Gradle では常に、すべての画面密度用のコードとリソースを含むユニバーサル APK が生成されます。
次の例では、各 ABI(x86
と x86_64
)用の APK を個別に生成しています。そのために、reset()
を使用して ABI の空のリストを作成してから、APK をそれぞれ取得する ABI のリストを include
で指定しています。
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 we only // want APKs for x86 and x86_64. // Resets the list of ABIs that Gradle should create APKs for to none. reset() // Specifies a list of ABIs that Gradle should create APKs for. include "x86", "x86_64" // Specifies that we do not 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 we only // want APKs for x86 and x86_64. // Resets the list of ABIs that Gradle should create APKs for to none. reset() // Specifies a list of ABIs that Gradle should create APKs for. include("x86", "x86_64") // Specifies that we do not want to also generate a universal APK that includes all ABIs. isUniversalApk = false } } }
サポート対象 ABI のリストについては、サポート対象 ABI をご覧ください。
mips、mips64、armeabi
Android Plugin for Gradle 3.1.0 以降において、mips
、mips64
、armeabi
の各 ABI の APK がデフォルトでは生成されなくなりました。これは、NDK r17 以降にこれらの ABI がサポート対象として含まれなくなったためです。
まず、Google Play Console で、これらの ABI をターゲットとするアプリの APK をダウンロードしているユーザーがいるかどうかを確認することを検討してください。このようなユーザーがいない場合は、これらの ABI をビルドから除外することをおすすめします。これらの ABI をターゲットとする APK のビルドを継続する場合は、NDK r16b 以前を使用して、アクティブなビルド バリアントとアクティブな ABI を設定し、下記のように build.gradle
ファイル内でターゲットの ABI を指定する必要があります。
Groovy
splits { abi { include 'armeabi', 'mips', 'mips64' ... } }
Kotlin
splits { abi { include ("armeabi", "mips", "mips64") ... } }
既知の問題: Android Plugin for Gradle 3.0.1 以前と NDK r17 以降を併用している場合、エラー(Error:ABIs [mips64, armeabi, mips] are not supported for platform.
)が発生することがあります。これは、ABI ごとの APK をビルドするときに、古いバージョンのプラグインにサポート対象外の ABI がデフォルトで依然として含まれるためです。この問題を解決するには、最新バージョンのプラグインに更新するか、以下のようにアプリの build.gradle
ファイルでプラグインのデフォルトの ABI リストをリセットし、必要なサポート対象の ABI のみを含めます。
Groovy
... splits { abi { ... reset() include "x86", "armeabi-v7a", "arm64-v8a", "x86_64" } }
Kotlin
... splits { abi { ... reset() include("x86", "armeabi-v7a", "arm64-v8a", "x86_64") } }
ネイティブ / C++ コードを含まないプロジェクト
[Build Variants] パネルには、[Module] と [Active Build Variant] の 2 つの列があります。モジュールの [Active Build Variant] 値により、デプロイされ、エディタで表示されるビルド バリアントが決定されます。
図 1: [Build Variants] パネルには、ネイティブ / C++ コードを含まないプロジェクト用の 2 つの列がある
バリアントを切り替えるには、モジュールの [Active Build Variant] セルをクリックし、リスト フィールドから目的のバリアントを選択します。
ネイティブ / C++ コードを含むプロジェクト
[Build Variants] パネルには、[Module]、[Active Build Variant]、[Active ABI] の 3 つの列があります。モジュールの [Active Build Variant] 値により、デプロイされ、エディタで表示されるビルド バリアントが決定されます。ネイティブ モジュールの場合は、[Active ABI] 値によってエディタが使用する ABI が決定されますが、デプロイされるものには影響しません。
図 2: ネイティブ / C++ コードを含むプロジェクトの場合は、[Build Variants] パネルに [Active ABI] 列が追加される
ビルドタイプまたは ABI を変更するには、[Active Build Variant] 列または [Active ABI] 列のセルをクリックし、リストから目的のバリアントまたは ABI を選択します。新しい同期が自動的に実行されます。アプリまたはライブラリ モジュールのいずれかの列を変更すると、すべての依存行に変更が適用されます。
バージョン管理の構成
Gradle で複数の APK を生成する場合、デフォルトでは、モジュール レベルの build.gradle
ファイルで指定されているバージョン情報がすべての APK に対して適用されます。Google Play ストアでは、同一アプリの複数の APK が同じバージョン情報を持つことは認められていないため、Play ストアにアップロードする前に、各 APK の versionCode
が一意の値になっていることを確認する必要があります。
モジュール レベルの build.gradle
ファイルは、各 APK の versionCode
をオーバーライドするように構成できます。出力のバージョン コードをオーバーライドするには、複数の APK を構成する ABI と画面密度のそれぞれに一意の数値を割り当てるマッピングを作成して、defaultConfig
または productFlavors
ブロック内で定義されたバージョン コードを画面密度または ABI に割り当てられた数値と組み合わせた値を使用します。
次の例では、x86
ABI の APK には versionCode
2004 が割り当てられ、x86_64
ABI の APK には 3004 が割り当てられます。1000 のように増分の大きなバージョン コードを割り当てると、アプリの更新が必要になったときに一意のバージョン コードを割り当てることができます。たとえば、その後のアップデートで defaultConfig.versionCode
が 5 に進められると、x86
APK には versionCode
2005 が、x86_64
APK には 3005 が Gradle によって割り当てられます。
ヒント: ビルドにユニバーサル 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 like this: // 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 does not override the version code for universal APKs. // However, because we 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 simply // 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 like this: // 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 does not override the version code for universal APKs. // However, because we 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] > [Build APK] をクリックして、[Project] ペインで現在選択されているモジュールの APK をすべてビルドします。Gradle で、各画面密度または ABI 用の APK が作成され、プロジェクトの build/outputs/apk/
ディレクトリに保存されます。
Gradle は、複数の APK を構成した画面密度または ABI ごとに APK をビルドします。
画面密度と ABI の両方で複数の APK を有効にすると、Gradle で画面密度と ABI の組み合わせごとに APK が作成されます。たとえば次の build.gradle
スニペットでは、mdpi と hdpi の画面密度および x86 と x86_64 の ABI に対して複数の APK のビルドが可能です。
Groovy
... splits { density { enable true reset() include "mdpi", "hdpi" } abi { enable true reset() include "x86", "x86_64" } }
Kotlin
... splits { density { enable true reset() include "mdpi", "hdpi" } abi { enable 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
ブロックで universalApk true
を指定すると、Gradle により、すべての ABI 用のコードとリソースを含む APK のみが生成されます。
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
になります。