Supportare gli aggiornamenti in-app (Kotlin o Java)

Questa guida descrive come supportare gli aggiornamenti in-app nella tua app utilizzando Kotlin o Java. Sono disponibili guide separate per i casi in cui la tua implementazione utilizza il codice nativo (C/C++) e per quelli in cui l'implementazione utilizza Unity.

Configura l'ambiente di sviluppo

La libreria di aggiornamenti in-app di Google Play fa parte delle librerie di base di Google Play. Includi la seguente dipendenza Gradle per integrare la libreria di aggiornamenti in-app di Play.

trendy

// 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")
    ...
}

Verifica la disponibilità di aggiornamenti

Prima di richiedere un aggiornamento, controlla se è disponibile un aggiornamento per la tua app. Utilizza AppUpdateManager per verificare la disponibilità di un aggiornamento:

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'istanza AppUpdateInfo restituita contiene lo stato di disponibilità dell'aggiornamento. A seconda dello stato dell'aggiornamento, l'istanza contiene anche quanto segue:

  • Se è disponibile un aggiornamento e l'aggiornamento è consentito, l'istanza contiene anche un intent per avviare l'aggiornamento.
  • Se è già in corso un aggiornamento in-app, l'istanza segnala anche lo stato dell'aggiornamento in corso.

Controlla il mancato aggiornamento degli aggiornamenti

Oltre a verificare se è disponibile un aggiornamento, potresti anche controllare quanto tempo è trascorso dall'ultima notifica all'utente tramite il Play Store. Questo può aiutarti a decidere se avviare un aggiornamento flessibile o un aggiornamento immediato. Ad esempio, potresti attendere alcuni giorni prima di inviare una notifica all'utente con un aggiornamento flessibile e alcuni giorni dopo prima di richiedere un aggiornamento immediato.

Utilizza clientVersionStalenessDays() per controllare il numero di giorni da quando l'aggiornamento è stato reso disponibile sul 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.
    }
});

Controlla la priorità degli aggiornamenti

L'API Google Play Developer ti consente di impostare la priorità di ogni aggiornamento. Ciò consente alla tua app di decidere in che misura consigliare un aggiornamento all'utente. Ad esempio, considera la seguente strategia per l'impostazione della priorità di aggiornamento:

  • Miglioramenti di minore entità all'interfaccia utente: aggiornamento a bassa priorità; non richiedere un aggiornamento flessibile né un aggiornamento immediato. Aggiornali solo quando l'utente non interagisce con l'app.
  • Miglioramenti delle prestazioni: aggiornamento con priorità media; richiedi un aggiornamento flessibile.
  • Aggiornamento per la sicurezza critico: aggiornamento con priorità elevata; richiedi un aggiornamento immediato.

Per determinare la priorità, Google Play utilizza un valore intero compreso tra 0 e 5, dove 0 è il valore predefinito e 5 è la priorità più alta. Per impostare la priorità di un aggiornamento, usa il campo inAppUpdatePriority in Edits.tracks.releases nell'API Google Play Developer. Tutte le nuove versioni aggiunte nella release vengono considerate con la stessa priorità della release. La priorità può essere impostata solo durante l'implementazione di una nuova release e non può essere modificata in un secondo momento.

Imposta la priorità utilizzando l'API Google Play Developer come descritto nella documentazione relativa all'API Play Developer. La priorità dell'aggiornamento in-app deve essere specificata nella risorsa Edit.tracks passata nel metodo Edit.tracks: update. L'esempio seguente illustra il rilascio di un'app con codice di versione 88 e inAppUpdatePriority 5:

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

Nel codice della tua app, puoi controllare il livello di priorità per un determinato aggiornamento utilizzando updatePriority(). La priorità restituita tiene conto del inAppUpdatePriority per tutti i codici versione dell'app tra la versione installata e l'ultima versione disponibile, indipendentemente dal canale di rilascio. Considera ad esempio il seguente scenario:

  • Rilascia la versione 1 per un canale di produzione senza priorità.
  • Rilascia la versione 2 per un canale di test interno con priorità 5.
  • Rilascia la versione 3 per un canale di produzione senza priorità.

Quando gli utenti di produzione eseguono l'aggiornamento dalla versione 1 alla versione 3, avranno la priorità 5, anche se la versione 2 è stata pubblicata in un canale diverso.

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

Avvia un aggiornamento

Dopo aver confermato che è disponibile un aggiornamento, puoi richiederlo utilizzando 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());

Ogni istanza AppUpdateInfo può essere utilizzata per avviare un aggiornamento una sola volta. Per riprovare a eseguire l'aggiornamento in caso di errore, richiedi un nuovo AppUpdateInfo e controlla nuovamente che l'aggiornamento sia disponibile e consentito.

Puoi registrare un'app per l'avvio dei risultati di attività utilizzando il contratto integrato ActivityResultContracts.StartIntentSenderForResult. Consulta la sezione su come ricevere il callback per lo stato di aggiornamento.

I passaggi successivi variano a seconda che tu richieda un aggiornamento flessibile o un aggiornamento immediato.

Configura un aggiornamento con AppUpdateOptions

AppUpdateOptions contiene un campo AllowAssetPackDeletion che definisce se durante l'aggiornamento è consentito cancellare i pacchetti di asset in caso di spazio di archiviazione limitato sul dispositivo. Questo campo è impostato su false per impostazione predefinita, ma puoi utilizzare il metodo setAllowAssetPackDeletion() per impostarlo su 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());

Ricevere una richiamata per lo stato dell'aggiornamento

Dopo l'avvio di un aggiornamento, il callback dell'Avvio app dei risultati di attività registrati ottiene il risultato della finestra di dialogo di conferma:

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

Esistono diversi valori che potresti ricevere dal callback onActivityResult():

  • RESULT_OK: l'utente ha accettato l'aggiornamento. Per aggiornamenti immediati, potresti non ricevere questo callback perché l'aggiornamento dovrebbe già essere completato dal controllo dell'ora restituito alla tua app.
  • RESULT_CANCELED: l'utente ha rifiutato o annullato l'aggiornamento.
  • ActivityResult.RESULT_IN_APP_UPDATE_FAILED: un altro errore ha impedito all'utente di fornire il consenso o il proseguimento dell'aggiornamento.

Gestire un aggiornamento flessibile

Quando avvii un aggiornamento flessibile, all'utente viene innanzitutto visualizzata una finestra di dialogo per richiedere il consenso. Se l'utente acconsente, il download inizia in background e l'utente può continuare a interagire con la tua app. In questa sezione viene descritto come monitorare e completare un aggiornamento in-app flessibile.

Monitoraggio dello stato dell'aggiornamento flessibile

Dopo l'inizio del download di un aggiornamento flessibile, la tua app deve monitorare lo stato di aggiornamento per sapere quando può essere installato e per visualizzare l'avanzamento nell'interfaccia utente dell'app.

Puoi monitorare lo stato di un aggiornamento in corso registrando un listener per gli aggiornamenti dello stato dell'installazione. Puoi anche fornire una barra di avanzamento nell'interfaccia utente dell'app per informare gli utenti dell'avanzamento del download.

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

Installa un aggiornamento flessibile

Quando rilevi lo stato InstallStatus.DOWNLOADED, devi riavviare l'app per installare l'aggiornamento.

A differenza degli aggiornamenti immediati, Google Play non attiva automaticamente il riavvio di un'app per un aggiornamento flessibile. Questo perché, durante un aggiornamento flessibile, l'utente si aspetta di continuare a interagire con l'app fino a quando non decide di installare l'aggiornamento.

Ti consigliamo di fornire una notifica (o qualche altra indicazione nell'interfaccia utente) per informare l'utente che l'aggiornamento è pronto per l'installazione e richiedere la conferma prima di riavviare l'app.

L'esempio seguente illustra l'implementazione di uno snackbar di Material Design che richiede all'utente la conferma del riavvio dell'app:

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

Quando chiami appUpdateManager.completeUpdate() in primo piano, la piattaforma mostra un'UI a schermo intero che riavvia l'app in background. Dopo che la piattaforma installa l'aggiornamento, l'app si riavvia nell'attività principale.

Se invece chiami completeUpdate() quando la tua app è in background, l'aggiornamento viene installato automaticamente senza occultare l'interfaccia utente del dispositivo.

Ogni volta che l'utente porta la tua app in primo piano, controlla se è presente un aggiornamento per l'app da installare. Se per la tua app è disponibile un aggiornamento nello stato DOWNLOADED, chiedi all'utente di installare l'aggiornamento. In caso contrario, i dati aggiornati continueranno a occupare lo spazio di archiviazione del dispositivo dell'utente.

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

Gestisci un aggiornamento immediato

Quando avvii un aggiornamento immediato e l'utente acconsente a avviarlo, Google Play mostra l'avanzamento dell'aggiornamento nella UI della tua app per l'intera durata dell'aggiornamento. Se l'utente chiude o termina la tua app durante l'aggiornamento, quest'ultimo deve continuare a essere scaricato e installato in background senza ulteriore conferma da parte dell'utente.

Tuttavia, quando la tua app torna in primo piano, devi verificare che l'aggiornamento non sia bloccato nello stato UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS. Se l'aggiornamento è bloccato in questo stato, riprendilo:

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

Il flusso di aggiornamento restituisce un risultato come descritto nella documentazione di riferimento per startUpdateFlowForResult(). In particolare, la tua app dovrebbe essere in grado di gestire i casi in cui un utente rifiuta l'aggiornamento o annulla il download. Quando l'utente esegue una di queste azioni, l'interfaccia utente di Google Play si chiude. La tua app dovrebbe determinare il modo migliore per procedere.

Se possibile, consenti all'utente di continuare senza l'aggiornamento e richiedi di nuovo l'aggiornamento in un secondo momento. Se la tua app non funziona senza l'aggiornamento, potresti visualizzare un messaggio informativo prima di riavviare il flusso di aggiornamento o di chiedere all'utente di chiudere l'app. In questo modo l'utente sarà consapevole di poter riavviare l'app quando sarà pronto a installare l'aggiornamento richiesto.

Passaggi successivi

Testa gli aggiornamenti in-app dell'app per verificare il corretto funzionamento dell'integrazione.