Play Feature Delivery

アプリは Play Feature Delivery を使用して、以下のデバイスにオンデマンドで機能モジュールをダウンロードできます。

  • Android 5.0(API レベル 21)以上を搭載したモバイル デバイスおよびタブレット
  • Chrome OS デバイス

アプリが必要な機能モジュールをダウンロードしてインストールするには、単に Play Core Library 内の API を呼び出すだけです。そうすれば、Google Play ストアがそのモジュールに必要なコードとリソースのみをデバイスにプッシュします。また、この API を使用して、Android Instant Apps 用のオンデマンド モジュールをダウンロードすることもできます。

最初に機能モジュールをプロジェクトに追加してオンデマンドで使用できるように設定する方法については、Play Feature Delivery の概要をお読みください。

さらに、このガイドを読み終わったら、Play Core API サンプルアプリを使って API の実際の動作を確認し、アプリ内アップデートをサポートする方法を学んでください。

最後に、アプリを公開する前に、App Bundle をテストして、アプリのオンデマンド機能が想定どおりに動作するかどうかを検証してください。

プロジェクトに Play Core Library を組み込む

はじめに、プロジェクトに Play Core Library をインポートする必要があります。

オンデマンド モジュールをリクエストする

アプリで機能モジュールを使用する必要があるときは、アプリが SplitInstallManager クラスを使用してフォアグラウンドで動作しているときにモジュールをリクエストできます。リクエストを行う際、アプリは、ターゲット モジュールのマニフェスト内の split 要素によって定義されているとおりにモジュールの名前を指定する必要があります。Android Studio で機能モジュールを作成すると、ビルドシステムは、指定されたモジュール名を使用して、コンパイル時にこのプロパティをモジュールのマニフェストに挿入します。詳しくは、機能モジュールのマニフェストをご覧ください。

たとえば、デバイスのカメラを使用して画像メッセージをキャプチャして送信するオンデマンド モジュールを備えたアプリがあり、このオンデマンド モジュールはマニフェスト内で split="pictureMessages" を指定しているとします。SplitInstallManager を使用して pictureMessages モジュール(ならびにプロモーション フィルタ用の追加モジュール)をリクエストするサンプルを以下に示します。

Kotlin

// Creates an instance of SplitInstallManager.
val splitInstallManager = SplitInstallManagerFactory.create(context)

// Creates a request to install a module.
val request =
    SplitInstallRequest
        .newBuilder()
        // You can download multiple on demand modules per
        // request by invoking the following method for each
        // module you want to install.
        .addModule("pictureMessages")
        .addModule("promotionalFilters")
        .build()

splitInstallManager
    // Submits the request to install the module through the
    // asynchronous startInstall() task. Your app needs to be
    // in the foreground to submit the request.
    .startInstall(request)
    // You should also be able to gracefully handle
    // request state changes and errors. To learn more, go to
    // the section about how to Monitor the request state.
    .addOnSuccessListener { sessionId -> ... }
    .addOnFailureListener { exception ->  ... }

Java

// Creates an instance of SplitInstallManager.
SplitInstallManager splitInstallManager =
    SplitInstallManagerFactory.create(context);

// Creates a request to install a module.
SplitInstallRequest request =
    SplitInstallRequest
        .newBuilder()
        // You can download multiple on demand modules per
        // request by invoking the following method for each
        // module you want to install.
        .addModule("pictureMessages")
        .addModule("promotionalFilters")
        .build();

splitInstallManager
    // Submits the request to install the module through the
    // asynchronous startInstall() task. Your app needs to be
    // in the foreground to submit the request.
    .startInstall(request)
    // You should also be able to gracefully handle
    // request state changes and errors. To learn more, go to
    // the section about how to Monitor the request state.
    .addOnSuccessListener(sessionId -> { ... })
    .addOnFailureListener(exception -> { ... });

アプリがオンデマンド モジュールをリクエストすると、Play Core Library は「ファイア アンド フォーゲット(撃ちっぱなし)」戦略を採用します。つまり、Play Core Library で、モジュールをプラットフォームにダウンロードするように求めるリクエストの送信はしますが、インストールが成功したかどうかのモニタリングはしません。インストール後にユーザー ジャーニーを続行するため、またはエラーを適切に処理するため、必ずリクエストの状態をモニタリングしてください。

注: デバイスにすでにインストールされている機能モジュールをリクエストしても問題ありません。API は、モジュールがインストール済みであることを検出すると、直ちにリクエストは完了したと判断します。また、モジュールは、インストールされた後 Google Play によって常に自動更新されます。つまり、App Bundle の新しいバージョンをアップロードすると、そのアプリに属するすべてのインストール済み APK がプラットフォームによって更新されます。詳しくは、アプリの更新を管理するをご覧ください。

モジュールのコードとリソースにアクセスするには、アプリで SplitCompat を有効にする必要があります。ただし、Android Instant Apps で SplitCompat は不要です。

オンデマンド モジュールのインストールを遅延する

アプリがオンデマンド モジュールをすぐにダウンロードしてインストールする必要がない場合は、インストールを遅延して、アプリがバックグラウンドで動作しているときにインストールできます。たとえば、アプリの次回の起動に備えてプロモーション コンテンツをプリロードしておく場合などがこれに該当します。

後でダウンロードするモジュールを指定するには、以下のように、deferredInstall() メソッドを呼び出します。また、SplitInstallManager.startInstall() とは異なり、アプリがフォアグラウンドで動作していなくても遅延インストールのリクエストを開始できます。

Kotlin

// Requests an on demand module to be downloaded when the app enters
// the background. You can specify more than one module at a time.
splitInstallManager.deferredInstall(listOf("promotionalFilters"))

Java

// Requests an on demand module to be downloaded when the app enters
// the background. You can specify more than one module at a time.
splitInstallManager.deferredInstall(Arrays.asList("promotionalFilters"));

遅延インストールのリクエストはベスト エフォート型のオペレーションで、進行状況をトラッキングすることはできません。したがって、遅延インストールを指定したモジュールにアクセスする前に、モジュールがインストール済みかどうかをチェックする必要があります。モジュールをすぐに利用可能にする必要がある場合は、上記のセクションで示しているように、代わりに SplitInstallManager.startInstall() を使用してモジュールをリクエストしてください。

リクエストの状態をモニタリングする

進行状況バーを更新したり、インストール後にインテントを起動したり、リクエスト エラーを適切に処理したりするには、状態に関する最新情報を非同期 SplitInstallManager.startInstall() タスクからリッスンする必要があります。インストール リクエストに関する最新情報を受信するには、リスナーを登録して、リクエストのセッション ID を取得します。以下をご覧ください。

Kotlin

// Initializes a variable to later track the session ID for a given request.
var mySessionId = 0

// Creates a listener for request status updates.
val listener = SplitInstallStateUpdatedListener { state ->
    if (state.sessionId() == mySessionId) {
      // Read the status of the request to handle the state update.
    }
}

// Registers the listener.
splitInstallManager.registerListener(listener)

...

splitInstallManager
    .startInstall(request)
    // When the platform accepts your request to download
    // an on demand module, it binds it to the following session ID.
    // You use this ID to track further status updates for the request.
    .addOnSuccessListener { sessionId -> mySessionId = sessionId }
    // You should also add the following listener to handle any errors
    // processing the request.
    .addOnFailureListener { exception ->
        // Handle request errors.
    }

// When your app no longer requires further updates, unregister the listener.
splitInstallManager.unregisterListener(listener)

Java

// Initializes a variable to later track the session ID for a given request.
int mySessionId = 0;

// Creates a listener for request status updates.
SplitInstallStateUpdatedListener listener = state -> {
    if (state.sessionId() == mySessionId) {
      // Read the status of the request to handle the state update.
    }
};

// Registers the listener.
splitInstallManager.registerListener(listener);

...

splitInstallManager
    .startInstall(request)
    // When the platform accepts your request to download
    // an on demand module, it binds it to the following session ID.
    // You use this ID to track further status updates for the request.
    .addOnSuccessListener(sessionId -> { mySessionId = sessionId; })
    // You should also add the following listener to handle any errors
    // processing the request.
    .addOnFailureListener(exception -> {
        // Handle request errors.
    });

// When your app no longer requires further updates, unregister the listener.
splitInstallManager.unregisterListener(listener);

リクエスト エラーを処理する

モジュールのダウンロードまたはインストールの失敗を適切に処理するには、addOnFailureListener() を使用する必要があります。以下をご覧ください。

Kotlin

splitInstallManager
    .startInstall(request)
    .addOnFailureListener { exception ->
        when ((exception as SplitInstallException).errorCode) {
            SplitInstallErrorCode.NETWORK_ERROR -> {
                // Display a message that requests the user to establish a
                // network connection.
            }
            SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED -> checkForActiveDownloads()
            ...
        }
    }

fun checkForActiveDownloads() {
    splitInstallManager
        // Returns a SplitInstallSessionState object for each active session as a List.
        .sessionStates
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                // Check for active sessions.
                for (state in task.result) {
                    if (state.status() == SplitInstallSessionStatus.DOWNLOADING) {
                        // Cancel the request, or request a deferred installation.
                    }
                }
            }
        }
}

Java

splitInstallManager
    .startInstall(request)
    .addOnFailureListener(exception -> {
        switch (((SplitInstallException) exception).getErrorCode()) {
            case SplitInstallErrorCode.NETWORK_ERROR:
                // Display a message that requests the user to establish a
                // network connection.
                break;
            case SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED:
                checkForActiveDownloads();
            ...
    });

void checkForActiveDownloads() {
    splitInstallManager
        // Returns a SplitInstallSessionState object for each active session as a List.
        .getSessionStates()
        .addOnCompleteListener( task -> {
            if (task.isSuccessful()) {
                // Check for active sessions.
                for (SplitInstallSessionState state : task.getResult()) {
                    if (state.status() == SplitInstallSessionStatus.DOWNLOADING) {
                        // Cancel the request, or request a deferred installation.
                    }
                }
            }
        });
}

アプリによる処理が必要となるエラー状態を以下の表に示します。

エラーコード 説明 推奨されるアクション
ACTIVE_SESSIONS_LIMIT_EXCEEDED 現在ダウンロード中の既存のリクエストが少なくとも 1 つ存在するため、このリクエストは拒否されました。 まだダウンロード中のリクエストが存在するかどうかをチェックします。上記のサンプルをご覧ください。
MODULE_UNAVAILABLE Google Play は、現在インストールされているアプリのバージョン、デバイス、ユーザーの Google Play アカウントに基づいて、リクエストされたモジュールを見つけることができませんでした。 ユーザーがモジュールに対するアクセス権限を持っていない場合は、ユーザーに通知します。
INVALID_REQUEST Google Play はリクエストを受信しましたが、リクエストが無効です。 リクエストに含まれている情報が完全で正確であることを確認します。
SESSION_NOT_FOUND 指定されたセッション ID のセッションが見つかりませんでした。 セッション ID でリクエストの状態をモニタリングする場合は、セッション ID が正しいことを確認します。
API_NOT_AVAILABLE 現在のデバイスで Play Core Library がサポートされていません。そのため、このデバイスはオンデマンドで機能をダウンロードしてインストールできません。 Android 4.4(API レベル 20)以前を搭載しているデバイスの場合、dist:fusing マニフェスト プロパティを使用して、インストール時に機能モジュールを組み込む必要があります。詳しくは、機能モジュールのマニフェストをご覧ください。
ACCESS_DENIED 権限が不十分なため、アプリがリクエストを登録できませんでした。 通常、このエラーはアプリがバックグラウンドで動作しているときに発生します。アプリがフォアグラウンドに戻ってから、リクエストを試行するようにします。
NETWORK_ERROR ネットワーク エラーのため、リクエストが失敗しました。 ネットワーク接続を確立するか、別のネットワークに変更することをユーザーに求めます。
INCOMPATIBLE_WITH_EXISTING_SESSION リクエスト内に、すでにリクエスト済みだがまだインストールされていないモジュールが 1 つまたは複数含まれています。 リクエスト済みのモジュールを含まない新しいリクエストを作成するか、現在リクエスト中のすべてのモジュールのインストールが完了するのを待ってからリクエストを再試行します。

なお、インストール済みのモジュールをリクエストしてもエラーにはなりません。

SERVICE_DIED リクエストの処理を担当するサービスが停止しています。 リクエストを再試行します。

SplitInstallStateUpdatedListener がこのエラーコードとともに SplitInstallSessionState を受け取ります。ステータスは FAILED、セッション ID は -1 です。

INSUFFICIENT_STORAGE 機能モジュールをインストールするにはデバイスの空き容量が不足しています。 この機能をインストールするには空き容量が不足していることをユーザーに通知します。
APP_NOT_OWNED このアプリは Google Play によってインストールされたものではないため、この機能をダウンロードできません。Play Core バージョン 1.9 以降でこのエラーが発生する可能性があるのは、遅延インストールの場合のみです。 Google Play からアプリを入手するには startInstall() を使用します。これにより、必要なユーザーの同意を得ることができます(Play Core バージョン 1.9 以降)。

ユーザーがオンデマンド モジュールのダウンロードをリクエストしてエラーが発生した場合は、再試行(リクエストを再試行する)とキャンセル(リクエストを破棄する)の 2 つの選択肢をユーザーに提示するダイアログを表示してください。また、サポートが必要な場合に備え、Google Play ヘルプセンターがユーザーに表示される [ヘルプ] リンクも提供してください。

ステータスの更新を処理する

リスナーを登録し、リクエストのセッション ID を記録したら、StateUpdatedListener.onStateUpdate() を使用して、状態の変更を処理します。以下をご覧ください。

Kotlin

override fun onStateUpdate(state : SplitInstallSessionState) {
    if (state.status() == SplitInstallSessionStatus.FAILED
        && state.errorCode() == SplitInstallErrorCode.SERVICE_DIED) {
       // Retry the request.
       return
    }
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            SplitInstallSessionStatus.DOWNLOADING -> {
              val totalBytes = state.totalBytesToDownload()
              val progress = state.bytesDownloaded()
              // Update progress bar.
            }
            SplitInstallSessionStatus.INSTALLED -> {

              // After a module is installed, you can start accessing its content or
              // fire an intent to start an activity in the installed module.
              // For other use cases, see access code and resources from installed modules.

              // If the request is an on demand module for an Android Instant App
              // running on Android 8.0 (API level 26) or higher, you need to
              // update the app context using the SplitInstallHelper API.
            }
        }
    }
}

Java

@Override
public void onStateUpdate(SplitInstallSessionState state) {
    if (state.status() == SplitInstallSessionStatus.FAILED
        && state.errorCode() == SplitInstallErrorCode.SERVICE_DIES) {
       // Retry the request.
       return;
    }
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            case SplitInstallSessionStatus.DOWNLOADING:
              int totalBytes = state.totalBytesToDownload();
              int progress = state.bytesDownloaded();
              // Update progress bar.
              break;

            case SplitInstallSessionStatus.INSTALLED:

              // After a module is installed, you can start accessing its content or
              // fire an intent to start an activity in the installed module.
              // For other use cases, see access code and resources from installed modules.

              // If the request is an on demand module for an Android Instant App
              // running on Android 8.0 (API level 26) or higher, you need to
              // update the app context using the SplitInstallHelper API.
        }
    }
}

インストール リクエストが取り得る状態を以下の表に示します。

リクエストの状態 説明 推奨されるアクション
PENDING リクエストはユーザーにより承認済みで、まもなくダウンロードが始まります。 進行状況バーなどの UI コンポーネントを初期化して、ダウンロードに関するフィードバックをユーザーに提供します。
REQUIRES_USER_CONFIRMATION ダウンロードにユーザーの同意が必要です。このステータスが発生する主な原因としては、アプリが Google Play からインストールされていないことが考えられます。 Google Play から機能をダウンロードすることについてユーザーに同意を求めます。 詳しくは、ユーザーの同意を取得する方法に関するセクションをご覧ください。
DOWNLOADING ダウンロード中です。 ダウンロードの進行状況バーを提供する場合は、SplitInstallSessionState.bytesDownloaded() メソッドと SplitInstallSessionState.totalBytesToDownload() メソッドを使用して UI を更新します(この表の上にあるコードサンプルを参照)。
DOWNLOADED デバイスがモジュールをダウンロードしましたが、インストールはまだ開始されていません。 ダウンロードしたモジュールを利用可能にしてこの状態が表示されないようにするには、アプリで SplitCompat を有効にする必要があります。このアクションは、機能モジュールのコードとリソースにアクセスするために必要です。
INSTALLING 現在、デバイスがモジュールをインストールしています。 進行状況バーを更新します。通常、この状態はすぐに終了します。
INSTALLED モジュールがデバイスにインストールされました。 モジュール内のコードとリソースにアクセスして、ユーザー ジャーニーを続行します。

Android 8.0(API レベル 26)以降で実行されている Android Instant App 用のモジュールの場合、splitInstallHelper を使用して新しいモジュールでアプリ コンポーネントを更新する必要があります。

FAILED デバイスにモジュールがインストールされる前に、リクエストが失敗しました。 リクエストを再試行するかキャンセルすることをユーザーに求めます。
CANCELING デバイスがリクエストをキャンセルしています。 詳しくは、インストール リクエストをキャンセルする方法に関するセクションをご覧ください。
CANCELED リクエストがキャンセルされました。

ユーザーの同意を取得する

ダウンロード リクエストに応じる際、Google Play がユーザーの同意を求めることがあります。たとえば、アプリが Google Play によってインストールされたものではない場合です。そのような場合は、リクエストのステータスとして REQUIRES_USER_CONFIRMATION が報告され、デバイスがリクエスト内のモジュールをダウンロードしてインストールするには、アプリがユーザーの同意を取得することが必要になります。ユーザーの同意を取得するには、アプリがユーザーにプロンプトを表示する必要があります。以下をご覧ください。

Kotlin

override fun onSessionStateUpdate(state: SplitInstallSessionState) {
    if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
        // Displays a confirmation for the user to confirm the request.
        splitInstallManager.startConfirmationDialogForResult(
          state,
          /* activity = */ this,
          // You use this request code to later retrieve the user's decision.
          /* requestCode = */ MY_REQUEST_CODE)
    }
    ...
 }

Java

@Override void onSessionStateUpdate(SplitInstallSessionState state) {
    if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
        // Displays a confirmation for the user to confirm the request.
        splitInstallManager.startConfirmationDialogForResult(
          state,
          /* activity = */ this,
          // You use this request code to later retrieve the user's decision.
          /* requestCode = */ MY_REQUEST_CODE);
    }
    ...
 }

ユーザーのレスポンスに応じて、リクエストのステータスが次のように更新されます。

  • ユーザーが同意すると、リクエスト ステータスが PENDING に変わり、ダウンロードが続行されます。
  • ユーザーが拒否すると、リクエスト ステータスは CANCELED に変わります。
  • ダイアログが破棄される前にユーザーが選択を行わなかった場合、リクエストのステータスは REQUIRES_USER_CONFIRMATION のままになります。アプリは、ユーザーにリクエストの完了を求めるプロンプトを再度表示できます。

ユーザーのレスポンスに応じてコールバックを受信するには、onActivityResult() を使用します。以下をご覧ください。

Kotlin

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
  if (requestCode == MY_REQUEST_CODE) {
    // Handle the user's decision. For example, if the user selects "Cancel",
    // you may want to disable certain functionality that depends on the module.
  }
}

Java

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
  if (requestCode == MY_REQUEST_CODE) {
    // Handle the user's decision. For example, if the user selects "Cancel",
    // you may want to disable certain functionality that depends on the module.
  }
}

インストール リクエストをキャンセルする

リクエストしたモジュールをアプリがインストールする前にリクエストをキャンセルする必要がある場合は、リクエストのセッション ID を使用して cancelInstall() メソッドを呼び出します。以下をご覧ください。

Kotlin

splitInstallManager
    // Cancels the request for the given session ID.
    .cancelInstall(mySessionId)

Java

splitInstallManager
    // Cancels the request for the given session ID.
    .cancelInstall(mySessionId);

モジュールにアクセスする

ダウンロードしたモジュールのコードとリソースにアクセスするには、アプリと、アプリがダウンロードした機能モジュール内の各アクティビティの両方で、SplitCompat ライブラリを有効にする必要があります。

ただし、モジュールのダウンロード後の一定期間(場合によっては数日)につき、プラットフォームにはモジュールのコンテンツへのアクセスに対して次の制限が課せられます。

  • プラットフォームは、モジュールによって導入される新しいマニフェスト エントリを適用できません。
  • プラットフォームは、通知など、システム UI コンポーネント用のモジュール リソースにはアクセスできません。そのようなリソースをすぐに使用する必要がある場合は、アプリのベース モジュールにリソースを組み込んでください。

SplitCompat を有効にする

ダウンロードしたモジュールのコードとリソースにアプリがアクセスするには、以下のセクションで説明している方法のいずれかを使用して SplitCompat を有効にする必要があります。

アプリに対して SplitCompat を有効にした後、アプリがアクセスする機能モジュール内で、アクティビティごとに SplitCompat を有効にする必要もあります。

マニフェスト内で SplitCompatApplication を宣言する

SplitCompat を有効にする最も簡単な方法は、アプリのマニフェスト内で、Application サブクラスとして SplitCompatApplication を宣言することです。以下をご覧ください。

<application
    ...
    android:name="com.google.android.play.core.splitcompat.SplitCompatApplication">
</application>

アプリがデバイスにインストールされると、ダウンロードした機能モジュールのコードとリソースに自動的にアクセスできます。

実行時に SplitCompat を呼び出す

実行時に特定のアクティビティまたはサービス内で SplitCompat を有効にすることもできます。機能モジュールに含まれるアクティビティを起動するには、この方法で SplitCompat を有効にする必要があります。そのためには、attachBaseContext をオーバーライドします。以下をご覧ください。

カスタム Application クラスを設定している場合は、代わりに SplitCompatApplication を継承することにより、アプリに対して SplitCompat を有効にします。以下をご覧ください。

Kotlin

class MyApplication : SplitCompatApplication() {
    ...
}

Java

public class MyApplication extends SplitCompatApplication {
    ...
}

SplitCompatApplicationは、単純に ContextWrapper.attachBaseContext() をオーバーライドして、SplitCompat.install(Context applicationContext) を組み込みます。カスタム Application クラスで SplitCompatApplication を継承する方法を使用したくない場合は、手動で attachBaseContext() メソッドをオーバーライドできます。以下をご覧ください。

Kotlin

override fun attachBaseContext(base: Context) {
    super.attachBaseContext(base)
    // Emulates installation of future on demand modules using SplitCompat.
    SplitCompat.install(this)
}

Java

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    // Emulates installation of future on demand modules using SplitCompat.
    SplitCompat.install(this);
}

オンデマンド モジュールが Instant App とインストール済みアプリの両方に対応している場合は、SplitCompat を条件付きで呼び出すことができます。以下をご覧ください。

Kotlin

override fun attachBaseContext(base: Context) {
    super.attachBaseContext(base)
    if (!InstantApps.isInstantApp(this)) {
        SplitCompat.install(this)
    }
}

Java

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    if (!InstantApps.isInstantApp(this)) {
        SplitCompat.install(this);
    }
}

モジュール アクティビティに対して SplitCompat を有効にする

ベースアプリに対して SplitCompat を有効にした後、機能モジュールでアプリがダウンロードするアクティビティごとに SplitCompat を有効にする必要があります。そのためには、SplitCompat.installActivity() メソッドを呼び出します。以下をご覧ください。

Kotlin

override fun attachBaseContext(base: Context) {
    super.attachBaseContext(base)
    // Emulates installation of on demand modules using SplitCompat.
    SplitCompat.installActivity(this)
}

Java

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    // Emulates installation of on demand modules using SplitCompat.
    SplitCompat.installActivity(this);
}

インストールしたモジュールのコードとリソースにアクセスする

ベースアプリのコンテキストと機能モジュール内のアクティビティに対して SplitCompat を有効にした場合、オンデマンド モジュールに対するリクエストが INSTALLED として報告されると、モジュールのコードとリソースをベース APK の一部であるかのように使用できるようになります。

新しくインストールしたモジュール内に存在するアセットまたはリソースにアプリの別のインストール済みモジュールからアクセスする場合は、アプリ コンテキストを使用してアクセスする必要があります。リソースにアクセスしようとするコンポーネントのコンテキストは、依然として更新されません。代わりに、機能モジュールのインストール後、対象コンポーネントの再作成または SplitCompat のインストールを行う方法もあります。

また、Android ApplicationInfo オブジェクト、そのコンテンツ、またはそれらを格納するオブジェクトをアプリ内にキャッシュしてはなりません。そのようなオブジェクトは、必要に応じて、常にアプリ コンテキストからフェッチする必要があります。このようなオブジェクトをキャッシュすると、Android の新しいバージョンに分割 APK をインストールする際にアプリがクラッシュする可能性があります。

インストール済みの Android Instant Apps にアクセスする

Android Instant App モジュールが INSTALLED として報告されたら、更新版のアプリ コンテキストを使用して、モジュールのコードとリソースにアクセスできます。モジュールをインストールする前にアプリが作成したコンテキスト(たとえば、すでに変数内に格納されているコンテキスト)には、新しいモジュールのコンテンツは含まれていません。新しいモジュールのコンテンツを含む更新版コンテキストを取得するには、たとえば、createPackageContext を使用します。

Kotlin

// Generate a new context as soon as a request for a new module
// reports as INSTALLED.
override fun onStateUpdate(state: SplitInstallSessionState ) {
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            ...
            SplitInstallSessionStatus.INSTALLED -> {
                val newContext = context.createPackageContext(context.packageName, 0)
                // If you use AssetManager to access your app’s raw asset files, you’ll need
                // to generate a new AssetManager instance from the updated context.
                val am = newContext.assets
            }
        }
    }
}

Java

// Generate a new context as soon as a request for a new module
// reports as INSTALLED.
@Override
public void onStateUpdate(SplitInstallSessionState state) {
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            ...
            case SplitInstallSessionStatus.INSTALLED:
                Context newContext = context.createPackageContext(context.getPackageName(), 0);
                // If you use AssetManager to access your app’s raw asset files, you’ll need
                // to generate a new AssetManager instance from the updated context.
                AssetManager am = newContext.getAssets();
        }
    }
}

Android 8.0 以上の Android Instant Apps

Android 8.0(API レベル 26)以降の Android Instant App のオンデマンド モジュールをリクエストする場合は、インストール リクエストが INSTALLED として報告された後、SplitInstallHelper.updateAppInfo(Context context) 呼び出しを通じて、新しいモジュールのコンテキストでアプリを更新する必要があります。そうしない限り、アプリはモジュールのコードとリソースを認識しません。アプリのメタデータを更新したら、新しい Handler を呼び出して、次回のメインスレッド イベント中にモジュールのコンテンツを読み込む必要があります。以下をご覧ください。

Kotlin

override fun onStateUpdate(state: SplitInstallSessionState ) {
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            ...
            SplitInstallSessionStatus.INSTALLED -> {
                // You need to perform the following only for Android Instant Apps
                // running on Android 8.0 (API level 26) and higher.
                if (BuildCompat.isAtLeastO()) {
                    // Updates the app’s context with the code and resources of the
                    // installed module.
                    SplitInstallHelper.updateAppInfo(context)
                    Handler().post {
                        // Loads contents from the module using AssetManager
                        val am = context.assets
                        ...
                    }
                }
            }
        }
    }
}

Java

@Override
public void onStateUpdate(SplitInstallSessionState state) {
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            ...
            case SplitInstallSessionStatus.INSTALLED:
            // You need to perform the following only for Android Instant Apps
            // running on Android 8.0 (API level 26) and higher.
            if (BuildCompat.isAtLeastO()) {
                // Updates the app’s context with the code and resources of the
                // installed module.
                SplitInstallHelper.updateAppInfo(context);
                new Handler().post(new Runnable() {
                    @Override public void run() {
                        // Loads contents from the module using AssetManager
                        AssetManager am = context.getAssets();
                        ...
                    }
                });
            }
        }
    }
}

C / C++ ライブラリを読み込む

デバイスが Instant App でダウンロードしたモジュールから C / C++ ライブラリを読み込む場合は、SplitInstallHelper.loadLibrary(Context context, String libName) を使用します。以下をご覧ください。

Kotlin

override fun onStateUpdate(state: SplitInstallSessionState) {
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            SplitInstallSessionStatus.INSTALLED -> {
                // Updates the app’s context as soon as a module is installed.
                val newContext = context.createPackageContext(context.packageName, 0)
                // To load C/C++ libraries from an installed module, use the following API
                // instead of System.load().
                SplitInstallHelper.loadLibrary(newContext, “my-cpp-lib”)
                ...
            }
        }
    }
}

Java

public void onStateUpdate(SplitInstallSessionState state) {
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            case SplitInstallSessionStatus.INSTALLED:
                // Updates the app’s context as soon as a module is installed.
                Context newContext = context.createPackageContext(context.getPackageName(), 0);
                // To load C/C++ libraries from an installed module, use the following API
                // instead of System.load().
                SplitInstallHelper.loadLibrary(newContext, “my-cpp-lib”);
                ...
        }
    }
}

インストールしたモジュールを管理する

デバイスに現在インストールされている機能モジュールを確認するには、SplitInstallManager.getInstalledModules() を呼び出します。これにより、インストール済みモジュールの名前の Set<String> が返されます。以下をご覧ください。

Kotlin

val installedModules: Set<String> = splitInstallManager.installedModules

Java

Set<String> installedModules = splitInstallManager.getInstalledModules();

モジュールをアンインストールする

モジュールのアンインストールをデバイスにリクエストするには、SplitInstallManager.deferredUninstall(List<String> moduleNames) を呼び出します。以下をご覧ください。

Kotlin

// Specifies two feature modules for deferred uninstall.
splitInstallManager.deferredUninstall(listOf("pictureMessages", "promotionalFilters"))

Java

// Specifies two feature modules for deferred uninstall.
splitInstallManager.deferredUninstall(Arrays.asList("pictureMessages", "promotionalFilters"));

モジュールのアンインストールは、すぐには実行されません。デバイスは、ストレージ容量を節約するため、必要に応じてバックグラウンドでモジュールをアンインストールします。上記のセクションで説明しているように、デバイスがモジュールを削除したかどうかを確認するには、SplitInstallManager.getInstalledModules() を呼び出してその結果を調べます。

追加の言語リソースをダウンロードする

App Bundle の場合、デバイスはアプリの実行に必要なコードとリソースのみをダウンロードします。したがって、言語リソースについては、ユーザーのデバイスは、デバイスの設定で現在選択されている 1 つ以上の言語と一致するアプリの言語リソースのみをダウンロードします。

アプリが追加の言語リソースにアクセスできるようにしたい場合(たとえばアプリ内言語選択ツールを実装する場合)は、Play Core Library を使用してオンデマンドでリソースをダウンロードできます。プロセスは、機能モジュールをダウンロードする場合と同様です。以下をご覧ください。

Kotlin

// Captures the user’s preferred language and persists it
// through the app’s SharedPreferences.
sharedPrefs.edit().putString(LANGUAGE_SELECTION, "fr").apply()
...

// Creates a request to download and install additional language resources.
val request = SplitInstallRequest.newBuilder()
        // Uses the addLanguage() method to include French language resources in the request.
        // Note that country codes are ignored. That is, if your app
        // includes resources for “fr-FR” and “fr-CA”, resources for both
        // country codes are downloaded when requesting resources for "fr".
        .addLanguage(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
        .build()

// Submits the request to install the additional language resources.
splitInstallManager.startInstall(request)

Java

// Captures the user’s preferred language and persists it
// through the app’s SharedPreferences.
sharedPrefs.edit().putString(LANGUAGE_SELECTION, "fr").apply();
...

// Creates a request to download and install additional language resources.
SplitInstallRequest request =
    SplitInstallRequest.newBuilder()
        // Uses the addLanguage() method to include French language resources in the request.
        // Note that country codes are ignored. That is, if your app
        // includes resources for “fr-FR” and “fr-CA”, resources for both
        // country codes are downloaded when requesting resources for "fr".
        .addLanguage(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
        .build();

// Submits the request to install the additional language resources.
splitInstallManager.startInstall(request);

このリクエストは、機能モジュールに対するリクエストと同様に処理されます。したがって、通常どおりリクエストの状態をモニタリングできます。

アプリが追加の言語リソースをすぐに必要としない場合は、インストールを遅延して、アプリがバックグラウンドで動作しているときにインストールできます。以下をご覧ください。

Kotlin

splitInstallManager.deferredLanguageInstall(
    Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))

Java

splitInstallManager.deferredLanguageInstall(
    Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));

ダウンロードした言語リソースにアクセスする

ダウンロードした言語リソースにアクセスするには、それらのリソースへのアクセスを必要とする各アクティビティの attachBaseContext() メソッド内で SplitCompat.installActivity() メソッドを実行する必要があります。以下をご覧ください。

Kotlin

override fun attachBaseContext(base: Context) {
  super.attachBaseContext(base)
  SplitCompat.installActivity(this)
}

Java

@Override
protected void attachBaseContext(Context base) {
  super.attachBaseContext(base);
  SplitCompat.installActivity(this);
}

アプリがダウンロードした言語リソースを使用するアクティビティごとに、ベース コンテキストを更新し、Configuration を通じて新しい言語 / 地域を設定します。

Kotlin

override fun attachBaseContext(base: Context) {
  val configuration = Configuration()
  configuration.setLocale(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
  val context = base.createConfigurationContext(configuration)
  super.attachBaseContext(context)
  SplitCompat.install(this)
}

Java

@Override
protected void attachBaseContext(Context base) {
  Configuration configuration = new Configuration();
  configuration.setLocale(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));
  Context context = base.createConfigurationContext(configuration);
  super.attachBaseContext(context);
  SplitCompat.install(this);
}

変更を有効にするには、新しい言語をインストールして使用する準備が整った後に、アクティビティを再作成する必要があります。Activity#recreate() メソッドを使用できます。

Kotlin

when (state.status()) {
  SplitInstallSessionStatus.INSTALLED -> {
      // Recreates the activity to load resources for the new language
      // preference.
      activity.recreate()
  }
  ...
}

Java

switch (state.status()) {
  case SplitInstallSessionStatus.INSTALLED:
      // Recreates the activity to load resources for the new language
      // preference.
      activity.recreate();
  ...
}

追加の言語リソースをアンインストールする

機能モジュールと同様、追加リソースはいつでもアンインストールできます。アンインストールをリクエストする前に、まず、現在インストールされている言語を確認しておくことをおすすめします。以下をご覧ください。

Kotlin

val installedLanguages: Set<String> = splitInstallManager.installedLanguages

Java

Set<String> installedLanguages = splitInstallManager.getInstalledLanguages();

次に、deferredLanguageUninstall() メソッドを使用して、アンインストールする言語を決定します。以下をご覧ください。

Kotlin

splitInstallManager.deferredLanguageUninstall(
    Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))

Java

splitInstallManager.deferredLanguageUninstall(
    Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));

参考情報

Play Core Library の使用方法については、以下のリソースをご覧ください。

サンプル

Codelab

ブログ投稿

動画