6 月 3 日の「#Android11: The Beta Launch Show」にぜひご参加ください。

アプリ内アップデートをサポートする

ユーザーのデバイスのアプリを最新の状態に保つと、ユーザーは新しい機能を試すことができ、パフォーマンスの向上とバグの修正というメリットを得られます。デバイスが定額制で接続されている間のみバックグラウンド アップデートを実施する設定をユーザーが有効にしている場合もありますが、アップデートの通知を必要とするユーザーもいます。アプリ内アップデートは Play Core Library の機能です。アクティブなユーザーにアプリのアップデートを促す新しいリクエスト フローを導入します。

アプリ内アップデートは Android 5.0(API レベル 21)以降のデバイスでのみ機能し、Play Core Library 1.5.0 以降の使用を必要とします。これらの要件を満たすと、アプリで以下のアプリ内アップデートの UX に対応できるようになります。

  • フレキシブル: バックグラウンドでのダウンロードとインストール、および正常状態の監視を行うユーザー エクスペリエンス。この UX は、ユーザーがアップデートのダウンロード中にアプリの使用が可能な場合に適しています。たとえば、アプリのコア機能にとって重要ではない新機能をユーザーに試してもらうように促す場合です。

    図 1. フレキシブルなアップデート フローの例

  • 即時: アプリの使用を継続するために、ユーザーがアプリを更新して再起動する必要があるフルスクリーンのユーザー エクスペリエンス。この UX は、アプリを継続して使用するためにアップデートが重要である場合に最適です。ユーザーが即時アップデートを承認すると、Google Play はアップデートのインストールとアプリの再起動を行います。

    図 2. 即時アップデート フローの例

このページでは、Play Core Library を使用して、アプリ内アップデート(フレキシブルまたは即時)をリクエストおよび実行する方法について説明します。

アップデートの有無の確認

アップデートをリクエストする前に、アプリで利用できるかどうかを最初に確認する必要があります。アップデートを確認するには、以下に示すように AppUpdateManagerを使用します。

Kotlin

    // Creates instance of the manager.
    val appUpdateManager = AppUpdateManagerFactory.create(context)

    // Returns an intent object that you use to check for an update.
    val appUpdateInfoTask = appUpdateManager.appUpdateInfo

    // Checks that the platform will allow the specified type of update.
    appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
        if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
            // For a flexible update, use AppUpdateType.FLEXIBLE
            && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
        ) {
            // Request the update.
        }
    }
    

Java

    // Creates instance of the manager.
    AppUpdateManager appUpdateManager = AppUpdateManagerFactory.create(context);

    // Returns an intent object that you use to check for an update.
    Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();

    // Checks that the platform will allow the specified type of update.
    appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
        if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
              // For a flexible update, use AppUpdateType.FLEXIBLE
              && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
                  // Request the update.
        }
    });
    

結果には、アップデートが利用可能かどうかについてのステータスが含まれます。アップデートが利用可能で、許可されている場合、返される AppUpdateInfo にはアップデートを開始するインテントも含まれます。アップデートを開始する方法については、次のセクションをご覧ください。

アプリ内アップデートがすでに進行中の場合、結果で進行中のアップデートのステータスも報告されます。

アップデートの開始

アプリを更新できることを確認したら、以下に示すように、AppUpdateManager.startUpdateFlowForResult() を使用してアップデートをリクエストできます。ただし、ユーザーのストレスにならないように、アップデートのリクエスト頻度に注意する必要があります。つまり、アプリ内アップデートのリクエストを、アプリの機能にとって重要な変更のみに制限する必要があります。

Kotlin

    appUpdateManager.startUpdateFlowForResult(
        // Pass the intent that is returned by 'getAppUpdateInfo()'.
        appUpdateInfo,
        // Or 'AppUpdateType.FLEXIBLE' for flexible updates.
        AppUpdateType.IMMEDIATE,
        // The current activity making the update request.
        this,
        // Include a request code to later monitor this update request.
        MY_REQUEST_CODE)
    

Java

    appUpdateManager.startUpdateFlowForResult(
        // Pass the intent that is returned by 'getAppUpdateInfo()'.
        appUpdateInfo,
        // Or 'AppUpdateType.FLEXIBLE' for flexible updates.
        AppUpdateType.IMMEDIATE,
        // The current activity making the update request.
        this,
        // Include a request code to later monitor this update request.
        MY_REQUEST_CODE);
    

AppUpdateInfo インスタンスを使用してアップデートを 1 回だけ開始できます。 失敗した場合にアップデートを再試行するには、新しい AppUpdateInfo をリクエストし、アップデートが利用可能かつ許可されていることを再度確認する必要があります。

リクエストするアップデートの種類によって、次に実行すべき手順が決まります。詳細については、即時アップデートの処理またはフレキシブル アップデートの処理に関するセクションをご覧ください。

アップデート ステータスのコールバックを取得する

アップデートを開始した後、以下に示すように、onActivityResult() コールバックを使用して、アップデートの失敗またはキャンセルに対処できます。

Kotlin

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
        if (requestCode == MY_REQUEST_CODE) {
            if (resultCode != RESULT_OK) {
                log("Update flow failed! Result code: $resultCode")
                // If the update is cancelled or fails,
                // you can request to start the update again.
            }
        }
    }
    

Java

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
      if (requestCode == MY_REQUEST_CODE) {
        if (resultCode != RESULT_OK) {
          log("Update flow failed! Result code: " + resultCode);
          // If the update is cancelled or fails,
          // you can request to start the update again.
        }
      }
    }
    

次に、onActivityResult() コールバックから受け取る可能性のあるさまざまな値について説明します。

  • RESULT_OK: ユーザーがアップデートを承認しました。即時アップデートの場合、アプリに制御が返されるまでに Google Play によってアップデートがすでに完了しているため、このコールバックを受信しない可能性があります。
  • RESULT_CANCELED: ユーザーがアップデートを拒否またはキャンセルしました。
  • ActivityResult.RESULT_IN_APP_UPDATE_FAILED: 他のエラーにより、ユーザーが同意できなかったか、アップデートを続行できませんでした。

フレキシブル アップデートの処理

フレキシブル アップデートを開始すると、ユーザーに同意を求めるダイアログが最初に表示されます。ユーザーが同意すると、バックグラウンドでダウンロードが開始され、ユーザーは引き続きアプリを操作できます。ここでは、アプリ内フレキシブル アップデートを監視および完了する方法について説明します。

フレキシブル アップデートの状態をモニタリングする

ユーザーがフレキシブル アップデートを承認すると、Google Play はバックグラウンドでアップデートのダウンロードを開始します。ダウンロードの開始後、アプリはアップデートの状態をモニタリングして、アップデートをインストールできるタイミングを把握し、アプリの UI に進行状況を表示する必要があります。

ステータス アップデートをインストールするリスナーを登録することにより、進行中のアップデートの状態をモニタリングできます。

Kotlin

    // Create a listener to track request state updates.
    val listener = { state ->
        // Show module progress, log state, or install the update.
    }

    // Before starting an update, register a listener for updates.
    appUpdateManager.registerListener(listener)

    // Start an update.

    // When status updates are no longer needed, unregister the listener.
    appUpdateManager.unregisterListener(listener)
    

Java

    // Create a listener to track request state updates.
    InstallStateUpdatedListener listener = state -> {
        // Show module progress, log state, or install the update.
      };

    // Before starting an update, register a listener for updates.
    appUpdateManager.registerListener(listener);

    // Start an update.

    // When status updates are no longer needed, unregister the listener.
    appUpdateManager.unregisterListener(listener);
    

フレキシブル アップデートをインストールする

フレキシブル アップデートの状態をモニタリングし、InstallStatus.DOWNLOADED の状態を検出するには、アプリを再起動してアップデートをインストールする必要があります。

即時アップデートとは異なり、Google Play はアプリの再起動を行いません。 これは、フレキシブル アップデートの間、ユーザーはアップデートのインストールを決定するまでアプリを使用し続けることを期待しているためです。

そのため、インストールの準備ができたことをユーザーに伝え、アプリの再起動に対するユーザーの確認を求める通知(またはその他の UI 表示)を行うことをおすすめします。

たとえば、図 1 に示すように、アプリを再起動するための確認をユーザーに求める、マテリアル デザインのスナックバーを実装できます。

次のコードサンプルは、フレキシブル アップデートがダウンロードされた後のユーザーへのスナックバーによる通知を示しています。

Kotlin

    override fun onStateUpdate(state: InstallState) {
        if (state.installStatus() == InstallStatus.DOWNLOADED) {
            // After the update is downloaded, show a notification
            // and request user confirmation to restart the app.
            popupSnackbarForCompleteUpdate()
        }
        ...
    }

    /* Displays the snackbar notification and call to action. */
    fun popupSnackbarForCompleteUpdate() {
        Snackbar.make(
            findViewById(R.id.activity_main_layout),
            "An update has just been downloaded.",
            Snackbar.LENGTH_INDEFINITE
        ).apply {
            setAction("RESTART") { appUpdateManager.completeUpdate() }
            setActionTextColor(resources.getColor(R.color.snackbar_action_text_color))
            show()
        }
    }
    

Java

    @Override
    public void onStateUpdate(InstallState state) {
      if (state.installStatus() == InstallStatus.DOWNLOADED) {
        // After the update is downloaded, show a notification
        // and request user confirmation to restart the app.
        popupSnackbarForCompleteUpdate();
      }
      ...
    }

    /* Displays the snackbar notification and call to action. */
    private void popupSnackbarForCompleteUpdate() {
      Snackbar snackbar =
          Snackbar.make(
              findViewById(R.id.activity_main_layout),
              "An update has just been downloaded.",
              Snackbar.LENGTH_INDEFINITE);
      snackbar.setAction("RESTART", view -> appUpdateManager.completeUpdate());
      snackbar.setActionTextColor(
          getResources().getColor(R.color.snackbar_action_text_color));
      snackbar.show();
    }
    

フォアグラウンドで appUpdateManager.completeUpdate() を呼び出すと、バックグラウンドでアプリを再起動するフルスクリーン UI がプラットフォームで表示されます。アップデートがインストールされると、アプリはメイン アクティビティで再起動します。

代わりにバックグラウンドで appUpdateManager.completeUpdate() を呼び出すと、デバイス UI を遮ることなく、アップデートがサイレント インストールされます。

ユーザーがアプリをフォアグラウンドにした場合、アプリにインストール待ちのアップデートがないことを確認することをおすすめします。アプリに DOWNLOADED 状態のアップデートがある場合、以下に示すように、ユーザーにアップデートをインストールするように求める通知を表示します。それ以外の場合、アップデート データは引き続きユーザーのデバイス ストレージに置かれます。

Kotlin

    // Checks that the update is not stalled during 'onResume()'.
    // However, you should execute this check at all app entry points.
    override fun onResume() {
        super.onResume()

        appUpdateManager
            .appUpdateInfo
            .addOnSuccessListener { appUpdateInfo ->
                ...
                // If the update is downloaded but not installed,
                // notify the user to complete the update.
                if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
                    popupSnackbarForCompleteUpdate()
                }
            }
    }
    

Java

    // Checks that the update is not stalled during 'onResume()'.
    // However, you should execute this check at all app entry points.
    @Override
    protected void onResume() {
      super.onResume();

      appUpdateManager
          .getAppUpdateInfo()
          .addOnSuccessListener(appUpdateInfo -> {
                  ...
                  // If the update is downloaded but not installed,
                  // notify the user to complete the update.
                  if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
                      popupSnackbarForCompleteUpdate();
                  }
              });
    }
    

即時アップデートの処理

即時アップデートを実行しており、ユーザーがアップデートのインストールに同意した場合、Google Play はアップデートが完了するまでの間、アップデートの進行状況をアプリの UI 上に表示します。アップデート中に、ユーザーがアプリを閉じた場合、または終了した場合、ユーザーへの確認は行われずに、アップデートが引き続きバックグラウンドでダウンロードおよびインストールされます。

ただし、アプリがフォアグラウンドに戻ったら、アップデートが UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS の状態で停止していないことを確認する必要があります。この状態でアップデートが停止している場合は、以下のようにしてアップデートを再開します。

Kotlin

    // Checks that the update is not stalled during 'onResume()'.
    // However, you should execute this check at all entry points into the app.
    override fun onResume() {
        super.onResume()

        appUpdateManager
            .appUpdateInfo
            .addOnSuccessListener { appUpdateInfo ->
                ...
                if (appUpdateInfo.updateAvailability()
                    == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS
                ) {
                    // If an in-app update is already running, resume the update.
                    appUpdateManager.startUpdateFlowForResult(
                        appUpdateInfo,
                        IMMEDIATE,
                        this,
                        MY_REQUEST_CODE
                    );
                }
            }
    }
    

Java

    // Checks that the update is not stalled during 'onResume()'.
    // However, you should execute this check at all entry points into the app.
    @Override
    protected void onResume() {
      super.onResume();

      appUpdateManager
          .getAppUpdateInfo()
          .addOnSuccessListener(
              appUpdateInfo -> {
                ...
                if (appUpdateInfo.updateAvailability()
                    == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
                    // If an in-app update is already running, resume the update.
                    appUpdateManager.startUpdateFlowForResult(
                        appUpdateInfo,
                        IMMEDIATE,
                        this,
                        MY_REQUEST_CODE);
                }
              });
    }
    

トラブルシューティング

このセクションでは、テスト中にアプリ内アップデートが期待どおりに動作しない場合に、考えられる解決策について説明します。

  • アプリ内アップデートは、アプリを所有するユーザー アカウントでのみ利用できます。そのため、アカウントを使用してアプリ内アップデートをテストする前に、使用しているアカウントで Google Play から少なくとも 1 回アプリをダウンロードできていることを確認してください。
  • アプリ内アップデートをテストするアプリのアプリケーション ID が同じであり、Google Play から入手できるものと同じ署名キーで署名されていることを確認してください。
  • Google Play によるアプリのアップデートは、より高いバージョン コードにのみ可能であるため、テストしているアプリがアップデートのバージョン コードよりも低いバージョン コードであることを確認してください。
  • アカウントが対象であり、Google Play キャッシュが最新であることを確認してください。これを行うには、テストデバイスで Google Play ストア アカウントにログインして、次の手順を実行します。
    1. 完全に Google Play ストア アプリを閉じます
    2. Google Play ストア アプリを開き、[マイアプリ&ゲーム] タブに移動します。
    3. テスト中のアプリに利用可能なアップデートが表示されない場合は、テスト版トラックの設定が適切であることを確認します。