Google は、黒人コミュニティに対する人種平等の促進に取り組んでいます。取り組みを見る

Play Core Library を使用してモジュールをダウンロードする

Google Play の Dynamic Delivery を使用しているアプリは、Android 5.0(API レベル 21)以降を搭載しているデバイスに対して、動的機能モジュールをオンデマンドでダウンロードできます。アプリ側で、必要に応じて Play Core Library 内の API を呼び出して、そのモジュールをダウンロードしてインストールするだけで、Google Play ストア側が、そのモジュールに必要なコードとリソースだけをデバイスにプッシュします。また、この API を使用して、Android Instant App 用のオンデマンド モジュールをダウンロードすることもできます。

先に動的機能モジュールをプロジェクトに追加しておいて、オンデマンドで使用できるように設定する方法については、動的機能モジュールを作成するをご覧ください。

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

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

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

Play Core Library の使用を開始するには、Gradle 依存関係としてアプリ モジュールにインポートする必要があります。以下をご覧ください。

    // In your app’s build.gradle file:
    ...
    dependencies {
        // This dependency is downloaded from the Google’s Maven repository.
        // So, make sure you also include that repository in your project's build.gradle file.
        implementation 'com.google.android.play:core:1.6.4'
        ...
    }
    

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

アプリが動的機能モジュールの使用を必要とするようになった場合、フォアグラウンドにあるときに 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 App の場合、SplitCompat は必要ありません。Android Instant App は、いつでもすぐに機能モジュールにアクセスできます。

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

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

後でダウンロードするモジュールを指定するには、以下のように、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 リクエストの処理を担当するサービスが停止しています。 リクエストを再試行してください。

このエラーコードは、ステータスが「FAILED」でセッション ID が「-1」の更新情報として SplitInstallStateUpdatedListener にエクスポーズされます。

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

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

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

Kotlin

    override fun onStateUpdate(state : SplitInstallSessionState) {
        if (state.status() == SplitInstallSessionStatus.FAILED
            && state.errorCode() == SplitInstallErrorCode.SERVICE_DIES) {
           // 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 ダウンロードにはユーザーの同意が必要です。ダウンロード サイズが 10 MB を超えている可能性があります。 ダウンロード リクエストを承認するよう、ユーザーに求めます。詳細については、ユーザーの同意を取得するをご覧ください。
DOWNLOADING ダウンロード中です。 ダウンロードの進行状況バーを提供する場合は、SplitInstallSessionState.bytesDownloaded() メソッドと SplitInstallSessionState.totalBytesToDownload() メソッドを使用して UI を更新します(この表の上にあるサンプルコードを参照)。
DOWNLOADED デバイスがモジュールをダウンロードしましたが、インストールはまだ開始されていません。 ダウンロードしたモジュールをすぐに利用できるようにし、この状態を回避するには、アプリに対して SplitCompat を有効にする必要があります。有効にしていない場合、そのダウンロードは INSTALLED に移行し、アプリがモジュールのコードとリソースを利用できるようになるのは、アプリがバックグラウンドに入った後になります。
INSTALLING 現在、デバイスがモジュールをインストールしています。 進行状況バーを更新します。通常、この状態はすぐに終了します。
INSTALLED モジュールがデバイスにインストールされました。 モジュール内のコードとリソースにアクセスして、ユーザー ジャーニーを続行します。

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

FAILED デバイスにモジュールがインストールされる前に、リクエストが失敗しました。 リクエストを再試行するか、キャンセルするよう、ユーザーに求めます。
CANCELING デバイスがリクエストをキャンセル中です。 詳細については、インストール リクエストをキャンセルするをご覧ください。
CANCELED リクエストがキャンセルされました。

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

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

Kotlin

    override fun onSessionStateUpdate(state: SplitInstallSessionState) {
        if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
            // Displays a dialog for the user to either “Download”
            // or “Cancel” 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 dialog for the user to either “Download”
            // or “Cancel” 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 を有効にした後、すぐにアプリから利用できるようにする動的機能モジュール内の各アクティビティに対しても、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 App にアクセスする

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 App

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++ ライブラリをロードする

デバイスがダウンロードしたモジュールから 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 dynamic feature modules for deferred uninstall.
    splitInstallManager.deferredUninstall(listOf("pictureMessages", "promotionalFilters"))
    

Java

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

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

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

Dynamic Delivery の場合、デバイスは、アプリの実行に必要なコードとリソースだけをダウンロードします。そのため、言語リソースに関しても、ユーザー デバイスは、デバイスの設定で現在選択されている 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 の使用方法については、以下のリソースをご覧ください。

サンプル

  • PlayCore API サンプル: PlayCore API を使用して動的機能をリクエスト、ダウンロードする例を示します。
  • 動的コードロード サンプル: インストールした動的機能モジュールのコードに安全にアクセスするための 3 つの方法を示します。

コードラボ

  • オンデマンド モジュール: オンデマンドで動的機能をダウンロード、インストールするアプリの作成方法について示します。

ブログ記事

動画