Prise en charge des mises à jour dans l'application (Kotlin ou Java)

Ce guide explique comment prendre en charge les mises à jour dans l'application à l'aide de Kotlin ou Java. Il existe des guides distincts pour les cas où votre implémentation utilise du code natif (C/C++) ou Unity.

Configurer l'environnement de développement

La bibliothèque Play In-App Update fait partie des bibliothèques Google Play Core. Veuillez inclure la dépendance Gradle suivante pour intégrer la bibliothèque Play In-App Update.

Groovy

// 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:app-update:2.1.0'

    // For Kotlin users also add the Kotlin extensions library for Play In-App Update:
    implementation 'com.google.android.play:app-update-ktx:2.1.0'
    ...
}

Kotlin

// In your app’s build.gradle.kts 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:app-update:2.1.0")

    // For Kotlin users also import the Kotlin extensions library for Play In-App Update:
    implementation("com.google.android.play:app-update-ktx:2.1.0")
    ...
}

Vérifier si une mise à jour est disponible

Avant de demander une mise à jour, vérifiez si une mise à jour est disponible pour votre application. Utilisez AppUpdateManager pour rechercher une mise à jour :

Kotlin

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
        // This example applies an immediate update. To apply a flexible update
        // instead, pass in AppUpdateType.FLEXIBLE
        && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
    ) {
        // Request the update.
    }
}

Java

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
          // This example applies an immediate update. To apply a flexible update
          // instead, pass in AppUpdateType.FLEXIBLE
          && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
              // Request the update.
    }
});

L'instance AppUpdateInfo renvoyée contient l'état de disponibilité de la mise à jour. Selon l'état de la mise à jour, l'instance contient également les éléments suivants :

  • Si une mise à jour est disponible et qu'elle est autorisée, l'instance contient également un intent permettant de lancer la mise à jour.
  • Si une mise à jour dans l'application est déjà en cours, l'instance signale également son statut.

Vérifier l'obsolescence des mises à jour

En plus de vérifier si une mise à jour est disponible, vous pouvez aussi consulter le temps écoulé depuis la dernière notification de mise à jour reçue par l'utilisateur via le Play Store. Cela peut vous aider à décider si vous devez lancer une mise à jour flexible ou immédiate. Par exemple, vous pouvez attendre quelques jours avant d'informer l'utilisateur d'une mise à jour flexible, puis quelques jours encore avant de demander une mise à jour immédiate.

Utilisez clientVersionStalenessDays() pour vérifier le nombre de jours écoulés depuis que la mise à jour est disponible sur le Play Store :

Kotlin

val appUpdateManager = AppUpdateManagerFactory.create(context)

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

// Checks whether the platform allows the specified type of update,
// and current version staleness.
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
    if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
          && (appUpdateInfo.clientVersionStalenessDays() ?: -1) >= DAYS_FOR_FLEXIBLE_UPDATE
          && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
              // Request the update.
    }
}

Java

AppUpdateManager appUpdateManager = AppUpdateManagerFactory.create(context);

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

// Checks whether the platform allows the specified type of update,
// and current version staleness.
appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
    if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
          && appUpdateInfo.clientVersionStalenessDays() != null
          && appUpdateInfo.clientVersionStalenessDays() >= DAYS_FOR_FLEXIBLE_UPDATE
          && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
              // Request the update.
    }
});

Vérifier la priorité des mises à jour

L'API Google Play Developer vous permet de définir la priorité de chaque mise à jour. Votre application peut ainsi déterminer le degré de recommandation d'une mise à jour auprès de l'utilisateur. Prenons la stratégie suivante pour exemple :

  • Améliorations mineures de l'interface utilisateur : mise à jour de faible priorité ; ne demandez ni une mise à jour flexible, ni une mise à jour immédiate. Effectuez la mise à jour uniquement lorsque l'utilisateur n'est pas en train d'interagir avec votre application.
  • Améliorations des performances : mise à jour de priorité moyenne ; demandez une mise à jour flexible.
  • Mise à jour de sécurité critique : mise à jour de priorité élevée ; demandez une mise à jour immédiate.

Pour déterminer la priorité, Google Play utilise un nombre entier compris entre 0 et 5, 0 étant la valeur par défaut et 5 étant la priorité la plus élevée. Pour définir la priorité d'une mise à jour, utilisez le champ inAppUpdatePriority sous Edits.tracks.releases dans l'API Google Play Developer. Toutes les nouvelles versions ajoutées sont considérées comme ayant la même priorité que la release. La priorité ne peut être définie que lors du déploiement d'une nouvelle release. Elle ne peut pas être modifiée ultérieurement.

Définissez la priorité à l'aide de l'API Google Play Developer, comme décrit dans la documentation de l'API Play Developer. La priorité de mise à jour dans l'application doit être spécifiée dans la ressource Edit.tracks qui est transmise via la méthode Edit.tracks: update. L'exemple suivant montre comment publier une application avec le code de version 88 et une inAppUpdatePriority de 5 :

{
  "releases": [{
      "versionCodes": ["88"],
      "inAppUpdatePriority": 5,
      "status": "completed"
  }]
}

Dans le code de votre application, vous pouvez vérifier le niveau de priorité d'une mise à jour donnée à l'aide de updatePriority(). La priorité renvoyée tient compte de inAppUpdatePriority pour tous les codes de version d'application entre la version installée et la dernière version disponible, quelle que soit la version. Prenons l'exemple suivant:

  • Vous publiez la version 1 sur un canal de production sans priorité.
  • Vous publiez la version 2 sur un canal de test interne avec la priorité 5.
  • Vous publiez la version 3 sur un canal de production sans priorité.

Lorsque les utilisateurs de production passent de la version 1 à la version 3, ils obtiennent la priorité 5, même si la version 2 a été publiée sur un canal différent.

Kotlin

val appUpdateManager = AppUpdateManagerFactory.create(context)

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

// Checks whether the platform allows the specified type of update,
// and checks the update priority.
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
    if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
          && appUpdateInfo.updatePriority() >= 4 /* high priority */
          && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
              // Request an immediate update.
    }
}

Java

AppUpdateManager appUpdateManager = AppUpdateManagerFactory.create(context);

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

// Checks whether the platform allows the specified type of update,
// and checks the update priority.
appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
    if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
          && appUpdateInfo.updatePriority() >= 4 /* high priority */
          && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
              // Request an immediate update.
    }
});

Commencer une mise à jour

Une fois que vous avez vérifié qu'une mise à jour est disponible, vous pouvez demander une mise à jour à l'aide de AppUpdateManager.startUpdateFlowForResult() :

Kotlin

appUpdateManager.startUpdateFlowForResult(
    // Pass the intent that is returned by 'getAppUpdateInfo()'.
    appUpdateInfo,
    // an activity result launcher registered via registerForActivityResult
    activityResultLauncher,
    // Or pass 'AppUpdateType.FLEXIBLE' to newBuilder() for
    // flexible updates.
    AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build())

Java

appUpdateManager.startUpdateFlowForResult(
    // Pass the intent that is returned by 'getAppUpdateInfo()'.
    appUpdateInfo,
    // an activity result launcher registered via registerForActivityResult
    activityResultLauncher,
    // Or pass 'AppUpdateType.FLEXIBLE' to newBuilder() for
    // flexible updates.
    AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build());

Chaque instance AppUpdateInfo ne peut être utilisée qu'une seule fois pour lancer une mise à jour. Pour relancer la mise à jour en cas d'échec, demandez un nouveau AppUpdateInfo et vérifiez à nouveau que la mise à jour est disponible et autorisée.

Vous pouvez enregistrer un lanceur de résultats d'activité à l'aide du contrat ActivityResultContracts.StartIntentSenderForResult intégré. Consultez la section sur l'appel de rappel pour l'état des mises à jour.

Les étapes suivantes varient selon que vous demandez une mise à jour flexible ou immédiate.

Configurer une mise à jour avec AppUpdateOptions

AppUpdateOptions contient un champ AllowAssetPackDeletion qui définit si la mise à jour est autorisée à effacer les packs d'éléments en cas d'espace de stockage limité sur l'appareil. Ce champ est défini sur false par défaut, mais vous pouvez utiliser la méthode setAllowAssetPackDeletion() pour le définir sur true:

Kotlin

appUpdateManager.startUpdateFlowForResult(
    // Pass the intent that is returned by 'getAppUpdateInfo()'.
    appUpdateInfo,
    // an activity result launcher registered via registerForActivityResult
    activityResultLauncher,
    // Or pass 'AppUpdateType.FLEXIBLE' to newBuilder() for
    // flexible updates.
    AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE)
        .setAllowAssetPackDeletion(true)
        .build())

Java

appUpdateManager.startUpdateFlowForResult(
    // Pass the intent that is returned by 'getAppUpdateInfo()'.
    appUpdateInfo,
    // an activity result launcher registered via registerForActivityResult
    activityResultLauncher,
    // Or pass 'AppUpdateType.FLEXIBLE' to newBuilder() for
    // flexible updates.
    AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE)
        .setAllowAssetPackDeletion(true)
        .build());

Recevoir un rappel pour l'état des mises à jour

Après avoir démarré une mise à jour, le rappel du lanceur de résultats d'activité enregistré obtient le résultat de la boîte de dialogue de confirmation:

Kotlin

registerForActivityResult(StartIntentSenderForResult()) { result: ActivityResult ->
    // handle callback
    if (result.resultCode != RESULT_OK) {
        log("Update flow failed! Result code: " + result.resultCode);
        // If the update is canceled or fails,
        // you can request to start the update again.
    }
}

Java

registerForActivityResult(
    new ActivityResultContracts.StartIntentSenderForResult(),
    new ActivityResultCallback<ActivityResult>() {
        @Override
        public void onActivityResult(ActivityResult result) {
            // handle callback
            if (result.getResultCode() != RESULT_OK) {
                log("Update flow failed! Result code: " + result.getResultCode());
                // If the update is canceled or fails,
                // you can request to start the update again.
            }
        }
    });

Le rappel onActivityResult() peut vous envoyer plusieurs valeurs :

  • RESULT_OK : l'utilisateur a accepté la mise à jour. Pour les mises à jour immédiates, il est possible que vous ne receviez pas ce rappel, car la mise à jour devrait déjà être terminée avant que le contrôle soit rendu à votre application.
  • RESULT_CANCELED : l'utilisateur a refusé ou annulé la mise à jour.
  • ActivityResult.RESULT_IN_APP_UPDATE_FAILED : une autre erreur a empêché l'utilisateur de donner son consentement ou a empêché la poursuite de la mise à jour.

Gérer une mise à jour flexible

Lorsque vous lancez une mise à jour flexible, l'utilisateur voit d'abord une boîte de dialogue lui demandant son consentement. Si l'utilisateur donne son consentement, le téléchargement démarre en arrière-plan et l'utilisateur peut continuer à interagir avec votre application. Cette section explique comment surveiller et effectuer une mise à jour flexible dans l'application.

Surveiller l'état de la mise à jour flexible

Une fois que le téléchargement d'une mise à jour flexible a commencé, votre application doit surveiller son état pour savoir quand la mise à jour peut être installée et pour afficher la progression dans l'interface utilisateur de votre application.

Vous pouvez surveiller l'état d'une mise à jour en cours en enregistrant un écouteur pour les mises à jour de l'état d'installation. Vous pouvez aussi fournir une barre de progression dans l'interface utilisateur de l'application pour informer les utilisateurs de la progression du téléchargement.

Kotlin

// Create a listener to track request state updates.
val listener = InstallStateUpdatedListener { state ->
    // (Optional) Provide a download progress bar.
    if (state.installStatus() == InstallStatus.DOWNLOADING) {
      val bytesDownloaded = state.bytesDownloaded()
      val totalBytesToDownload = state.totalBytesToDownload()
      // Show update progress bar.
    }
    // 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 -> {
  // (Optional) Provide a download progress bar.
  if (state.installStatus() == InstallStatus.DOWNLOADING) {
      long bytesDownloaded = state.bytesDownloaded();
      long totalBytesToDownload = state.totalBytesToDownload();
      // Implement progress bar.
  }
  // 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);

Installer une mise à jour flexible

Lorsque vous détectez l'état InstallStatus.DOWNLOADED, vous devez redémarrer l'application pour installer la mise à jour.

Contrairement aux mises à jour immédiates, Google Play ne déclenche pas automatiquement le redémarrage d'une application dans le cas d'une mise à jour flexible. En effet, lors d'une mise à jour flexible, l'utilisateur s'attend à pouvoir continuer à interagir avec l'application jusqu'à ce qu'il décide d'installer la mise à jour.

Nous vous recommandons de fournir une notification (ou toute autre indication dans l'interface utilisateur) pour informer l'utilisateur que la mise à jour est prête à être installée et lui demander confirmation avant de redémarrer l'application.

L'exemple suivant montre comment implémenter un snackbar Material Design demandant à l'utilisateur de confirmer le redémarrage de l'application :

Kotlin

val listener = { 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.
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

InstallStateUpdatedListener listener = 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();
}

Lorsque vous appelez appUpdateManager.completeUpdate() au premier plan, la plate-forme affiche une interface utilisateur plein écran qui redémarre l'application en arrière-plan. Une fois que la plate-forme a installé la mise à jour, votre application redémarre dans son activité principale.

Si vous appelez completeUpdate() lorsque votre application est en arrière-plan, la mise à jour est installée de façon silencieuse, sans masquer l'interface utilisateur de l'appareil.

Chaque fois que l'utilisateur place votre application au premier plan, vérifiez si une mise à jour est en attente d'installation. Si l'état de mise à jour de votre application est DOWNLOADED, invitez l'utilisateur à installer la mise à jour. Sinon, les données de mise à jour continuent à occuper l'espace de stockage de l'appareil de l'utilisateur.

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();
              }
          });
}

Gérer une mise à jour immédiate

Lorsque vous lancez une mise à jour immédiate et que l'utilisateur l'accepte, Google Play affiche la progression de la mise à jour sur l'interface utilisateur de votre application pendant toute la durée de la mise à jour. Si l'utilisateur ferme ou arrête votre application pendant la mise à jour, le téléchargement et l'installation doivent se poursuivre en arrière-plan sans confirmation supplémentaire de l'utilisateur.

Toutefois, lorsque votre application revient au premier plan, vous devez vérifier que la mise à jour n'est pas bloquée à l'état UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS. Si la mise à jour est bloquée dans cet état, reprenez-la :

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,
                  activityResultLauncher,
                  AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build())
            }
        }
}

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,
                  activityResultLauncher,
                  AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build());
            }
          });
}

Le flux de mise à jour renvoie un résultat comme décrit dans la documentation de référence pour startUpdateFlowForResult(). En particulier, votre application doit pouvoir gérer les cas où un utilisateur refuse la mise à jour ou annule le téléchargement. Lorsque l'utilisateur effectue l'une de ces actions, l'interface utilisateur de Google Play se ferme. Votre application doit déterminer la meilleure façon de procéder.

Si possible, laissez l'utilisateur continuer sans la mise à jour et invitez-le à installer la mise à jour ultérieurement. Si votre application ne peut pas fonctionner sans la mise à jour, envisagez d'afficher un message informatif avant de redémarrer le flux de mise à jour ou d'inviter l'utilisateur à fermer l'application. Ainsi, l'utilisateur comprend qu'il peut redémarrer votre application lorsqu'il est prêt à installer la mise à jour requise.

Étapes suivantes

Testez les mises à jour dans votre application pour vérifier le bon fonctionnement de l'intégration.