Supportare gli aggiornamenti in-app (Kotlin o Java)

Questa guida descrive come supportare gli annunci in-app aggiornamenti nell'app usando Kotlin o Java. Esistono guide separate per i casi in cui l'implementazione utilizza codice nativo (C/C++) e i casi in cui utilizza Unity.

Configura l'ambiente di sviluppo

La libreria di aggiornamento in-app di Play fa parte delle librerie di base di Google Play. Includi la seguente dipendenza da Gradle per integrare la campagna in-app di Google Play Aggiorna raccolta.

Alla moda

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

Verificare la disponibilità degli aggiornamenti

Prima di richiedere un aggiornamento, controlla se è disponibile un aggiornamento per la tua app. Utilizza le funzionalità di AppUpdateManager per controllare se è disponibile 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'oggetto restituito AppUpdateInfo contiene lo stato di disponibilità dell'aggiornamento. In base allo stato , l'istanza contiene anche quanto segue:

  • Se è disponibile un aggiornamento, che è consentito, anche l'istanza contiene l'intento di avviare l'aggiornamento.
  • Se è già in corso un aggiornamento in-app, l'istanza segnala anche stato dell'aggiornamento in corso.

Controlla lo stato di inattività degli aggiornamenti

Oltre a verificare la disponibilità di un aggiornamento, controlla quanto tempo è trascorso dall'ultima notifica di un aggiornamento all'utente tramite il Play Store. Questo può aiutarti a decidere se devi avviare o meno flessibile o immediato. Ad esempio, potresti attendere alcuni giorni prima di informare l'utente con un aggiornamento flessibile e alcuni giorni dopo prima di richiedere un aggiornamento immediato.

Utilizza le funzionalità di clientVersionStalenessDays() per controllare il numero di giorni dall'introduzione dell'aggiornamento 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. In questo modo la tua app può decidere in che misura consigliare un aggiornamento all'utente. Ad esempio, considera la seguente strategia per impostare la priorità degli aggiornamenti:

  • Miglioramenti di minore entità all'interfaccia utente: aggiornamento a priorità bassa; non richiede uno spazio né un aggiornamento immediato. Aggiorna solo quando l'utente non interagisce con la tua app.
  • Miglioramenti delle prestazioni: aggiornamento a priorità media; richiedere un piano flessibile aggiornamento.
  • Aggiornamento critico della sicurezza: aggiornamento ad alta priorità; richiedi un accesso immediato aggiornamento.

Per determinare la priorità, Google Play utilizza un valore intero compreso tra 0 e 5, con 0 il valore predefinito e 5 la priorità massima. Per impostare la priorità di un aggiorna, usa il campo inAppUpdatePriority sotto Edits.tracks.releases nella API Google Play Developer. Tutte le versioni appena aggiunte nella release con la stessa priorità dell'uscita. La priorità può essere impostata solo quando una nuova release e non può essere modificata in un secondo momento.

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

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

Nel codice dell'app puoi controllare il livello di priorità di un determinato aggiornamento utilizzando updatePriority() La priorità restituita tiene conto di inAppUpdatePriority per tutte le app i codici versione dalla versione installata all'ultima versione disponibile, a prescindere 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, hanno la priorità. 5, anche se la versione 2 è stata pubblicata su un altro canale.

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 la disponibilità di un aggiornamento, puoi richiederne uno 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 di AppUpdateInfo può essere utilizzata per avviare un aggiornamento una sola volta. Per riprovare In caso di errore l'aggiornamento, richiedi un nuovo AppUpdateInfo e ricontrolla che l'aggiornamento sia disponibile e consentito.

Puoi registrare un'Avvio app di risultati di attività usando la ActivityResultContracts.StartIntentSenderForResult contratto. Consulta la sezione Ricevere la richiamata per lo stato dell'aggiornamento.

I passaggi successivi dipendono dal fatto che tu stia richiedendo un modello aggiornamento o immediato.

Configura un aggiornamento con AppUpdateOptions

AppUpdateOptions contiene un campo AllowAssetPackDeletion che definisce se l'aggiornamento è possono 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 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());

Ricevi una richiamata per lo stato dell'aggiornamento

Dopo l'avvio di un aggiornamento, il callback dell'Avvio app dei risultati delle attività registrate riceve 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 da onActivityResult() callback:

  • RESULT_OK: l'utente ha ha accettato l'aggiornamento. Per aggiornamenti immediati, potresti non ricevere perché l'aggiornamento dovrebbe essere già terminato quando il controllo dell'ora dati 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 aggiornamento per non continuare.

Gestire un aggiornamento flessibile

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

Monitora lo stato di aggiornamento flessibile

Dopo l'inizio del download per un aggiornamento flessibile, l'app deve monitorare per sapere quando è possibile installare l'aggiornamento e per visualizzare di avanzamento nell'UI dell'app.

Puoi monitorare lo stato di un aggiornamento in corso registrando un listener per aggiornamenti sullo stato dell'installazione. Puoi anche fornire una barra di avanzamento nell'interfaccia utente dell'app per informare gli utenti sull'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 il per installare l'aggiornamento.

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

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

Il seguente esempio dimostra l'implementazione di un Material Design una snackbar che richiede Conferma da parte dell'utente di riavviare l'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 una UI a schermo intero che riavvia l'app. in background. Dopo che la piattaforma ha installato l'aggiornamento, l'app viene riavviata in la sua attività principale.

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

Ogni volta che l'utente porta la tua app in primo piano, controlla se l'app ha un aggiornamento in attesa di essere installato. Se è disponibile un aggiornamento per la tua app in DOWNLOADED richiede all'utente di installare l'aggiornamento. Altrimenti, i dati di aggiornamento continuano 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();
              }
          });
}

Gestire un aggiornamento immediato

Quando avvii un aggiornamento immediato e l'utente acconsente a avviarlo, Google Play mostra l'avanzamento dell'aggiornamento nella parte superiore dell'interfaccia utente dell'app in tutto il l'intera durata dell'aggiornamento. Se l'utente chiude o chiude la tua app durante l'aggiornamento, dovrebbe 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 la proprietà l'aggiornamento non si blocca nella UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS stato. 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 deve poter gestire i casi in cui un utente rifiuta la richiesta. aggiorna o annulla il download. Quando l'utente esegue una di queste azioni, l'interfaccia utente di Google Play si chiude. La tua app deve determinare il modo migliore per procedere.

Se possibile, consenti all'utente di continuare senza l'aggiornamento e invia la richiesta di nuovo in un secondo momento. Se l'app non può funzionare senza l'aggiornamento, valuta la possibilità di visualizzare un messaggio informativo prima di riavviare il flusso di aggiornamento o di chiedere all'utente di chiudi l'app. In questo modo, l'utente capisce che può riavviare la tua app quando sono pronti a installare l'aggiornamento richiesto.

Passaggi successivi

Testare gli aggiornamenti in-app dell'app per effettuare la verifica che l'integrazione funzioni correttamente.