複数の APK をビルドする

アプリを 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 を使用することで、ldpixxhdpi にのみ画面密度のリストを設定しています。
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 と一致しないデバイスにはフォールバックを提供する必要があります。

次の例では、サポート対象画面の範囲に記載されている各画面密度(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 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 を使用することで、x86x86_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(x86x86_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 以降において、mipsmips64armeabi の各 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 になります。