Play Feature Delivery の概要

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

Play Feature Delivery は App Bundle の高度な機能を使用することで、アプリの部分的な機能を条件付きで配信したり、オンデマンドで提供したりできます。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

アプリをモジュール化する

アプリのモジュール化とは、アプリ プロジェクトの論理コンポーネントを個別のモジュールに分割するプロセスのことです。

アプリの各機能を個別のコンポーネントに再編成するには、綿密な検討を行い時間を確保する必要があります。それでもモジュール化によって、プロジェクトには以下の利点がもたらされます。

  • 並行して開発: アプリの論理コンポーネントをモジュールに分割することで、各モジュールを組織内の異なるチームまたは個人が所有して担当し、他のチームとの統合時における競合や障害を減らすことができます。さらに、アプリのさまざまな部分で使用されるロジックがある場合は、ライブラリ モジュールを使用して、コードの再利用とカプセル化を促すことができます。
  • ビルド時間に関する改善: Gradle を使用する Android Studio などのビルドシステムは、モジュールとして編成されているプロジェクトを対象として最適化されています。たとえば、Gradle のプロジェクトの並列実行による最適化を、マルチコア プロセッサが搭載されたワークステーションで有効にする場合、ビルドシステムは複数のモジュールを並行してビルドし、ビルドの所要時間を大幅に削減できます。プロジェクトのモジュール化が進むほど、ビルドのパフォーマンスが大幅に向上します。
  • 機能の配信のカスタマイズ: アプリの機能を機能モジュールとしてモジュール化することは、Play Feature Delivery のカスタム配信方法(オンデマンド配信、条件付き配信、Instant 配信など)を活用するための要件です。オンデマンドの機能を作成するには、さらに労力をかけ、アプリで可能なリファクタリングを進める必要があります。そのため、どのアプリの機能を機能モジュールにモジュール化すればカスタム配信方法のメリットが最大になるかを、慎重に検討してください。

アプリの機能ごとにプロジェクトをモジュール化するには、時間をかけて、適切な方法を検討する必要があります。アプリのモジュール化を始めることを決断したら、まず、モジュール式機能をサポートするのに必要なプロパティを指定して、ベース モジュールを構成する必要があります。その後で、インストール時配信用に機能モジュールを構成することで、アプリの現在の動作を変えずにアプリの機能を段階的にモジュール化できます。

機能モジュールのマニフェスト

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>
    

Play Feature Delivery をテストする

Play Feature Delivery のテストは Google Play ストアから行うことをおすすめします。これは、Play Feature Delivery のメリットの多くが、APK の最適化された生成、署名、Play ストアへの配信の遅延処理に依存しているためです。したがって、単に App Bundle をアップロードする場合または高度な配信方法を構成する場合のいずれであっても、以下の方法でアプリをテストするようにしてください。

リソースの 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 の既知の問題を必ずご確認ください。

参考情報

Play Feature Delivery のサポートの詳細については、以下のリソースをご覧ください。

サンプル

Codelab

  • 初めての Android App Bundle: Android App Bundle の基本原則を探り、Android Studio を使用して独自のビルドをすぐに始める方法を示すコードラボです。この Codelab では、bundletool を使用して App Bundle をテストする方法についても紹介します。
  • オンデマンド モジュール: オンデマンドで機能モジュールをダウンロードしてインストールするアプリの作成方法を紹介します。

ブログ投稿

動画