複数の APK のビルド

アプリを Google Play に公開する場合、Android App Bundle を作成してアップロードする必要があります。こうすることで、各ユーザーのデバイス設定に合わせて最適化された APK が Google Play によって自動的に生成、配信されるため、ユーザーはアプリの実行に必要なコードとリソースをダウンロードするだけで済みます。複数の APK の公開は、Google Play に公開しない場合は便利ですが、各 APK のビルド、署名、管理を自分で行う必要があります。

可能であれば、すべての対象デバイスをサポートする 1 つの 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 に設定すると、定義した画面密度に基づいて複数の APK が Gradle によって生成されます。デフォルト値は 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 つの画面密度を削除しています。

    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'
        }
      }
    }
    

画面密度名と画面サイズ名のリストについては、複数画面をサポートする方法をご覧ください。特定の画面タイプおよびデバイスへのアプリの配信について詳しくは、特定の画面のデバイスへの配信をご覧ください。

ABI 用の複数の APK の設定

さまざまな ABI 用の APK を個別に作成するには、 splits ブロック内に abi ブロックを追加します。abi ブロックで、目的の ABI のリストを指定します。

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

enable
この要素を true に設定すると、定義した ABI に基づいて複数の APK が Gradle によって生成されます。デフォルト値は 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 をカンマ区切りのリストで指定します。ABI の正確なリストを指定するには、reset() と組み合わせて使用する必要があります。
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 で指定しています。

    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
        }
      }
    }
    

サポート対象 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 以前を使用して、以下に示すように build.gradle ファイルで ABI を指定する必要があります。

    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 のみを含めます。

    ...
    splits {
        abi {
            ...
            reset()
            include "x86", "armeabi-v7a", "arm64-v8a", "x86_64"
        }
    }
    

バージョン管理の設定

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 をオーバーライドしないことによって、これに対処しています。

    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
        }
      }
    }
    

別のバージョン コード スキームの例については、バージョン コードの割り当てをご覧ください。

複数の 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 のビルドが可能です。

    ...
      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 になります。