Play Feature Delivery の概要

Google Play のアプリ配信モデルは、Android App Bundle を使用して、個別のユーザーのデバイス設定に合わせて最適化された APK を生成して配信します。これにより、ユーザーはアプリの実行に必要なコードとリソースのみをダウンロードできます。

Play Feature Delivery は App Bundle の高度な機能を使用することで、アプリの特定の機能を条件付きで配信する、またはオンデマンドでダウンロード可能にすることができます。そのためには、まずそれらの機能をベースアプリから分離し、機能モジュールにする必要があります。

機能モジュールのビルド構成

Android Studio を使用して新しい機能モジュールを作成する際、IDE によって次の Gradle プラグインがモジュールの build.gradle ファイルに適用されます。

// The following applies the dynamic-feature plugin to your feature module.
// The plugin includes the Gradle tasks and properties required to configure and build
// an app bundle that includes your feature module.

apply plugin: 'com.android.dynamic-feature'

標準のアプリ プラグインで使用できるプロパティの多くは、機能モジュールでも使用できます。以下のセクションでは、機能モジュールのビルド構成に含めるべきプロパティと含めるべきでないプロパティについて説明します。

機能モジュールのビルド構成に含めるべきでないプロパティ

各機能モジュールはベース モジュールに依存しているため、特定の構成も継承します。したがって、機能モジュールの build.gradle ファイルから次の項目を除外する必要があります。

  • 署名設定: App Bundle はベース モジュールで指定する署名設定を使って署名されます。
  • minifyEnabled プロパティ: ベース モジュールのビルド構成からのみ、アプリ プロジェクト全体に対するコード圧縮を有効にすることができます。したがって、このプロパティを機能モジュールから除外する必要があります。ただし、機能モジュールごとに追加の ProGuard ルールを指定できます。
  • versionCodeversionName: App Bundle をビルドする際、Gradle はベース モジュールが提供するアプリ バージョン情報を使用します。したがって、これらのプロパティを機能モジュールの build.gradle ファイルから除外する必要があります。

ベース モジュールとの関係を確立する

Android Studio は、機能モジュールを作成する際、以下に示すように android.dynamicFeatures プロパティをベース モジュールの build.gradle ファイルに追加することにより、ベース モジュールから機能モジュールを認識できるようにします。

// In the base module’s build.gradle file.
android {
    ...
    // Specifies feature modules that have a dependency on
    // this base module.
    dynamicFeatures = [":dynamic_feature", ":dynamic_feature2"]
}

さらに以下に示すように、Android Studio は機能モジュールの依存関係としてベース モジュールを格納します。

// In the feature module’s build.gradle file:
...
dependencies {
    ...
    // Declares a dependency on the base module, ':app'.
    implementation project(':app')
}

追加の ProGuard ルールを指定する

アプリ プロジェクトのコード圧縮を有効にできるのはベース モジュールのビルド構成のみですが、proguardFiles プロパティを使用して、機能モジュールごとにカスタム ProGuard ルールを指定できます。下記をご覧ください。

android.buildTypes {
     release {
         // You must use the following property to specify additional ProGuard
         // rules for feature modules.
         proguardFiles 'proguard-rules-dynamic-features.pro'
     }
}

これらの ProGuard ルールは、ビルド時に他のモジュール(ベース モジュールを含む)のルールと統合されます。したがって、機能モジュールごとに新しいルールセットを指定することはできますが、それらのルールはアプリ プロジェクト内のすべてのモジュールに適用されます。

アプリをデプロイする

機能モジュールをサポートするアプリの開発中も、通常のように、接続されたデバイスにアプリをデプロイできます。それには、メニューバーから [Run] > [Run] を選択します(またはツールバーで実行アイコン をクリックします)。

アプリ プロジェクトに機能モジュールが含まれる場合、アプリのデプロイ時にどの機能を含めるかを選択できます。それには、次のようにして既存の実行 / デバッグ構成を変更します。

  1. メニューバーから [Run] > [Edit Configurations] を選択します。
  2. [Run/Debug Configurations] ダイアログの左パネルの [Android App] で該当するアプリの構成を選択します。
  3. [General] タブの [Dynamic features to deploy] で、アプリのデプロイ時に含める各機能モジュールのチェックボックスをオンにします。
  4. [OK] をクリックします。

デフォルトでは、Android Studio が App Bundle を使用してアプリをデプロイすることはありません。代わりに IDE が APK をビルドしてデバイスにインストールします。この APK は、サイズよりも、デプロイの早さについて最適化されます。IDE ではなく、Android Studio で App Bundle からの APK と Instant 版のビルド、デプロイを行うように構成するには、実行 / デバッグ構成を変更します。

機能モジュールをカスタム配信に使用する

機能モジュールの特長は、Android 5.0(API レベル 21)以上を搭載したデバイスに対して、アプリのさまざまな機能をどのように、どのタイミングでダウンロードするかをカスタマイズできるところにあります。たとえば、アプリの初回ダウンロード サイズを削減するために、一部の機能を必要に応じてオンデマンドでダウンロードしたり、特定の機能(写真の撮影機能、拡張現実のサポート機能など)を持つデバイスにのみダウンロードしたりするように構成できます。

アプリを App Bundle としてアップロードすれば、デフォルトでも高度に最適化されたダウンロードが可能になりますが、さらに高度でカスタマイズされた機能配信オプションを利用するには、「機能モジュール」を使ってアプリの機能をモジュール化する追加的な設定が必要になります。つまり、機能モジュールは、必要に応じたダウンロードを可能にするモジュール式機能を作成するための構成要素を提供します。

オンライン ショップでユーザーが商品を売買するためのアプリについて考えてみましょう。アプリの以下の機能は、それぞれ別の機能モジュールに合理的にモジュール化できます。

  • アカウントのログインと作成
  • ショップのブラウジング
  • 販売する商品の配置
  • 支払いの処理

下の表は、機能モジュールがサポートするさまざまな配信方法と、それぞれサンプルのショップアプリの初回ダウンロード サイズをどのように最適化するかを示しています。

配信方法 動作 ユースケース 導入方法
インストール時配信 上述の配信方法のいずれも設定しない機能モジュールが、デフォルトでアプリのインストール時にダウンロードされます。これは、高度な配信方法を段階的に採用できることを意味するため、重要な動作です。たとえば、Play Core ライブラリを使用してオンデマンド ダウンロードを完全に実装した後にのみ、アプリ機能のモジュール化を活用して、オンデマンド配信を有効にできます。

さらに、アプリは後で機能のアンインストールをリクエストできます。 それにより、アプリのインストール時に必要な特定の機能がそれ以降は不要な場合に、デバイスからその機能を削除するリクエストをすることで、インストール サイズを削減できます。

ショップでの商品の売買方法に関するインタラクティブ ガイドのような特定のトレーニング アクティビティがアプリにある場合は、アプリのインストール時にデフォルトでその機能を含めることができます。

一方、アプリのインストール サイズを削減するために、ユーザーがトレーニングを完了した後で、アプリは機能の削除をリクエストできます。

高度な配信方法を構成しない機能モジュールを使用して、アプリをモジュール化します。

ユーザーにとって不要になった特定の機能モジュールを削除することで、アプリのインストール サイズを削減する方法については、インストールしたモジュールを管理するをご覧ください。

オンデマンド配信 必要に応じて機能モジュールをリクエストおよびダウンロードすることをアプリに許可します。 ショップアプリの利用者のうち、販売する商品を投稿するユーザーが全体の 20% のみである場合、大多数のユーザー向けに初回ダウンロード サイズを削減する適切な戦略は、写真の撮影、商品の説明の投稿、販売する商品の掲載といった機能をオンデマンド ダウンロードとして利用可能にすることです。つまり、アプリの販売機能の機能モジュールは、ユーザーが販売する商品をショップに掲載することに関心を示したときにのみダウンロードできるように構成できます。

さらに、一定期間ユーザーが商品を販売しなかった場合、アプリは販売機能のアンインストールをリクエストすることで、インストール済みのサイズを削減できます。

機能モジュールを作成して、オンデマンド配信を構成します。その後、アプリは Play Core ライブラリを使用して、オンデマンドでモジュールをダウンロードするようリクエストできます。
条件付き配信 ハードウェアの機能、言語、地域、最小 API レベルなど、特定のユーザー デバイス要件を指定することで、モジュール化した機能をダウンロードするかどうかを、アプリのインストール時に判断可能にすることができます。 ショップアプリがグローバル ユーザーを対象とする場合、一部の地域や言語でのみ頻繁に使用される支払い方法のサポートが必要になることがあります。アプリの初回ダウンロード サイズを削減するために、特定のタイプの支払い方法を処理する機能モジュールを個別に作成して、ユーザーのデバイスに、登録されている言語や地域に基づく条件付きでインストールされるようにできます。 機能モジュールを作成して、条件付き配信を構成します。
Instant 配信 Google Play Instant を使用すると、デバイスにアプリをインストールしなくても、ユーザーがアプリを利用できるようになります。インストールする代わりに、ユーザーは Google Play ストアの [今すぐ試す] ボタン、またはデベロッパーが作成した URL を使用してアプリを利用できます。このコンテンツ配信形式を使用すると、Android アプリのユーザー エンゲージメントを高めるのが容易になります。

Instant 配信で Google Play Instant を活用すると、ユーザーはインストールなしでアプリの一部の機能をすぐに試すことができます。

ゲームの最初のいくつかのレベルを軽量の機能モジュールに含める場合を考えてみましょう。そのモジュールで Instant に対応すると、ユーザーはアプリをインストールしなくても、URL リンクまたは [今すぐ試す] ボタンからすぐにゲームを体験できます。 機能モジュールを作成して、Instant 配信を構成します。その後、アプリは Play のコアライブラリを使用して、オンデマンドでモジュールをダウンロードするようリクエストできます。

機能モジュールを使用したアプリ機能のモジュール化は最初のステップにすぎません。Google Play Instant をサポートするには、アプリのベース モジュールと Instant 対応にした特定の機能のダウンロード サイズが厳格なサイズ要件を満たす必要があります。詳細については、アプリまたはゲームのサイズを削減して Instant 機能を有効にするをご覧ください。

リソースの URI を作成する

URI を使用して機能モジュールに保存されているリソースにアクセスする場合は、次に示すように、Uri.Builder() を使用して機能モジュールのリソース URI を生成します。

Kotlin

val uri = Uri.Builder()
                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
                .authority(context.getPackageName()) // Look up the resources in the application with its splits loaded
                .appendPath(resources.getResourceTypeName(resId))
                .appendPath(String.format("%s:%s",
                  resources.getResourcePackageName(resId), // Look up the dynamic resource in the split namespace.
                  resources.getResourceEntryName(resId)
                  ))
                .build()

Java

String uri = Uri.Builder()
                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
                .authority(context.getPackageName()) // Look up the resources in the application with its splits loaded
                .appendPath(resources.getResourceTypeName(resId))
                .appendPath(String.format("%s:%s",
                  resources.getResourcePackageName(resId), // Look up the dynamic resource in the split namespace.
                  resources.getResourceEntryName(resId)
                  ))
                .build().toString();

分割 APK が読み込まれた後に正しい名前空間が生成されるように、リソースへのパスの各部分は実行時に作成されます。

URI の生成方法の例として、次の名前のアプリと機能モジュールを考えてみましょう。

  • アプリのパッケージ名: com.example.my_app_package
  • 機能のリソース パッケージ名: com.example.my_app_package.my_dynamic_feature

上記のコード スニペットの resId が、機能モジュールの「my_video」という名前の未加工のファイル リソースを参照した場合、上記の Uri.Builder() コードの出力は次のようになります。

android.resource://com.example.my_app_package/raw/com.example.my_app_package.my_dynamic_feature:my_video

アプリはこの URI を使用して、機能モジュールのリソースにアクセスできます。

URI のパスを検証するには、APK Analyzer を使用して機能モジュール APK を検査し、パッケージ名を特定します。

コンパイル済みリソース ファイルの内容を検査する APK Analyzer のスクリーンショット。

図 2. APK Analyzer を使用してコンパイル済みリソース ファイルのパッケージ名を検査する

機能モジュールについての検討事項

機能モジュールを使用すると、ビルド速度とエンジニアリング速度が向上し、アプリの配信を広範囲にカスタマイズすることでアプリのサイズを縮小できます。ただし、機能モジュールを使用する際には、いくつかの制約とエッジケースに留意してください。

  • 条件付き配信やオンデマンド配信で、1 台のデバイスに 50 個以上の機能モジュールをインストールすると、パフォーマンスの問題が発生する可能性があります。インストール時モジュール(リムーバブルとして構成されていないもの)は、自動的にベース モジュールに追加されて、各デバイスで 1 つの機能モジュールとしてカウントされます。
  • インストール時配信用に削除可能として構成するモジュールの数を 10 個以下に制限します。そうしないと、アプリのダウンロードとインストールに要する時間が増大する可能性があります。
  • オンデマンドでの機能のダウンロードとインストールに対応しているのは、Android 5.0(API レベル 21)以上を搭載したデバイスのみです。それより前のバージョンの Android で機能を利用可能にするには、機能モジュールの作成時に融合を有効にしてください。
  • ダウンロードされた機能モジュールにアプリがアクセスできるようにするため、SplitCompat を有効にします
  • 機能モジュールのマニフェストで android:exportedtrue に設定したアクティビティを指定しないでください。これは、別のアプリがそのアクティビティを開始しようとした際に、デバイスが機能モジュールをダウンロード済みであるという保証がないためです。さらに、アプリは機能のコードとリソースにアクセスしようとする前に、その機能がダウンロード済みであることを確認する必要があります。詳細については、インストールしたモジュールの管理をご覧ください。
  • Play Feature Delivery は App Bundle を使用してアプリを公開する必要があるため、App Bundle の既知の問題を必ずご確認ください。

機能モジュールのマニフェスト リファレンス

Android Studio を使用して新しい機能モジュールを作成する際、機能モジュールとして動作するモジュールに必要なほとんどのマニフェスト属性は IDE に含まれています。また、一部の属性はコンパイル時にビルドシステムによって挿入されるため、ご自分で指定または変更する必要はありません。次の表に、機能モジュールの重要なマニフェスト属性を示します。

属性 説明
<manifest
...
これは典型的な <manifest> ブロックです。
xmlns:dist="http://schemas.android.com/apk/distribution" 新しい dist: XML 名前空間を指定します。これについては以下で詳細に説明します。
split="split_name" Android Studio が App Bundle をビルドする際、この属性が自動的に含まれます。そのため、この属性をご自分で指定または変更する必要はありません

モジュールの名前を定義します。これは、Play Core ライブラリを使用してオンデマンド モジュールをリクエストする際に、アプリで指定するものです。

Gradle がこの属性の値を判断する方法:

デフォルトでは、Android Studio を使用して機能モジュールを作成する際、IDE はモジュール名として指定された名前を、Gradle 設定ファイル内の Gradle サブプロジェクトとしてのモジュールを識別する名前として使用します。

App Bundle をビルドする際に、Gradle はサブプロジェクト パスの最後の要素を使用して、このマニフェスト属性をモジュールのマニフェストに挿入します。たとえば、新しい機能モジュールを MyAppProject/features/ ディレクトリに作成し、モジュール名として「dynamic_feature1」を指定した場合、IDE は ':features:dynamic_feature1' をサブプロジェクトとして settings.gradle ファイルに追加します。App Bundle をビルドすると、Gradle はモジュールのマニフェストに <manifest split="dynamic_feature1"> を挿入します。

android:isFeatureSplit="true | false"> Android Studio が App Bundle をビルドする際、この属性が自動的に含まれます。したがって、この属性を手動で指定または変更する必要はありません

このモジュールが機能モジュールであることを指定します。ベース モジュールと構成 APK のマニフェストでは、この属性は省略するか、false に設定します。

<dist:module この新しい XML 要素は、モジュールを APK としてパッケージ化して配信する方法を決定する属性を定義します。
dist:instant="true | false" モジュールを Google Play Instant を介して Instant 機能として利用できるようにするかどうかを指定します。

アプリに Instant 対応の機能モジュールが含まれる場合は、ベース モジュールも Instant 対応にする必要があります。Android Studio 3.5 以上を使用して、Instant 対応の機能モジュールを作成すると、IDE が Instant 対応を自動的に有効にします。

<dist:on-demand/> も設定している場合、この XML 要素を true に設定することはできません。ただし、その場合でも Play Core ライブラリを使用して、Instant 対応の機能モジュールを「Instant 機能」としてオンデマンドでダウンロードすることをリクエストできます。ユーザーがアプリをダウンロードしてインストールする際、デフォルトではデバイスはアプリの Instant 対応機能モジュールをベース APK とともにダウンロードしてインストールします。

dist:title="@string/feature_name" モジュールのユーザー向けのタイトルを指定します。たとえば、デバイスがダウンロードの確認を求める際にこのタイトルが表示される場合があります。

このタイトルの文字列リソースをベース モジュールの module_root/src/source_set/res/values/strings.xml ファイルに追加する必要があります。

<dist:fusing dist:include="true | false" />
</dist:module>
Android 4.4(API レベル 20)以前を搭載したデバイスを対象とするマルチ APK 内に、対象のモジュールを含めるかどうかを指定します。

さらに、bundletool を使用して App Bundle から APK を生成する際は、このプロパティを true に設定する機能モジュールのみがユニバーサル APK に組み込まれます。ユニバーサル APK は、アプリがサポートするすべてのデバイス設定用のコードとリソースを含むモノリシック APK です。

<dist:delivery> 次に示すように、モジュール配信をカスタマイズするオプションをカプセル化します。各機能モジュールでは、以下のカスタム配信方法のうち 1 つのタイプのみを構成するようにしてください。
<dist:install-time> モジュールをインストール時に利用可能にすることを指定します。これは、別のタイプのカスタム配信オプションを指定しない機能モジュールのデフォルトの動作です。

インストール時ダウンロードについて詳しくは、インストール時の配信を設定するをご覧ください。

このノードでは、デバイスの機能、ユーザーの居住国、最小 API レベルなど、特定の要件を満たすデバイスにモジュールを制限する条件も指定できます。詳細については、条件付き配信の構成をご覧ください。

<dist:removable dist:value="true | false" />

未設定にするか、false に設定すると、バンドルから分割 APK を生成する際に、bundletool によってインストール時モジュールがベース モジュールに融合されます。融合の結果、分割 APK の数が減るため、この設定によってアプリのパフォーマンスが向上する可能性があります。

removabletrue に設定されている場合、インストール時モジュールはベース モジュールに融合されません。将来、モジュールをアンインストールする場合は、true に設定します。ただし、削除可能に設定したモジュールが多すぎると、アプリのインストール時間が長くなる可能性があります。

デフォルトは false です。この値をマニフェストで設定する必要があるのは、機能モジュールの融合を無効にしたい場合のみです。

注: この機能は、Android Gradle プラグイン 4.2 を使用している場合か、コマンドラインから bundletool v1.0 を使用している場合にのみ使用できます。

</dist:install-time>  
<dist:on-demand/> モジュールをオンデマンドでダウンロード可能にすることを指定します。つまり、モジュールはインストール時には利用できませんが、後でアプリがダウンロードをリクエストできます。

オンデマンド ダウンロードの詳細については、オンデマンド配信を構成するをご覧ください。

</dist:delivery>
<application
android:hasCode="true | false">
...
</application>
DEX ファイルを生成しない機能モジュールの場合は、後で DEX ファイル形式にコンパイルされるコードが含まれていないため、以下のように設定する必要があります(そうしないとランタイム エラーが発生する可能性があります)。
  1. 機能モジュールのマニフェストで android:hasCode"false" に設定します。
  2. ベース モジュールのマニフェストに以下の行を追加します。
    
    <application
      android:hasCode="true"
      tools:replace="android:hasCode">
      ...
    </application>
    

参考情報

機能モジュールの使用の詳細については、以下のリソースをご覧ください。

ブログ投稿

動画