Questa guida descrive come supportare gli aggiornamenti in-app nella tua app utilizzando Kotlin o Java. Esistono guide separate per i casi in cui l'implementazione utilizza codice nativo (C/C++) e per i casi in cui l'implementazione utilizza Unity.
Configura l'ambiente di sviluppo
La libreria di aggiornamento in-app di Google Play fa parte delle librerie di base di Google Play. Includi la seguente dipendenza Gradle per integrare la Libreria di aggiornamento in-app di Google Play.
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") ... }
Verificare la disponibilità di aggiornamenti
Prima di richiedere un aggiornamento, controlla se è disponibile un aggiornamento per la tua app.
Utilizza
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'istanza
AppUpdateInfo
ritornata contiene lo stato di disponibilità dell'aggiornamento. A seconda dello stato dell'aggiornamento, l'istanza contiene anche quanto segue:
- Se è disponibile un aggiornamento e questo è consentito, l'istanza contiene anche un'intenzione per avviarlo.
- Se è già in corso un aggiornamento in-app, l'istanza riporta anche lo stato dell'aggiornamento in corso.
Controllare l'obsolescenza degli aggiornamenti
Oltre a verificare se è disponibile un aggiornamento, ti consigliamo di controllare anche quanto tempo è trascorso dall'ultima notifica di aggiornamento inviata all'utente tramite il Play Store. In questo modo, puoi decidere se avviare un aggiornamento flessibile o immediato. Ad esempio, potresti attendere qualche giorno prima di inviare una notifica all'utente con un aggiornamento flessibile e qualche giorno dopo prima di richiedere un aggiornamento immediato.
Utilizza
clientVersionStalenessDays()
per controllare il numero di giorni trascorsi dall'aggiornamento 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. } });
Controllare la priorità dell'aggiornamento
L'API Google Play Developer ti consente di impostare la priorità di ogni aggiornamento. In questo modo, la tua app può decidere con quale insistenza consigliare un aggiornamento all'utente. Ad esempio, considera la seguente strategia per impostare la priorità di aggiornamento:
- Miglioramenti secondari all'interfaccia utente: aggiornamento a bassa priorità; non richiedere un aggiornamento flessibile né un aggiornamento immediato. Aggiorna solo quando l'utente non interagisce con la tua app.
- Miglioramenti delle prestazioni: aggiornamento con priorità media; richiedi un aggiornamento flessibile.
- Aggiornamento della sicurezza critico: 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 update, utilizza il campo inAppUpdatePriority
in Edits.tracks.releases
nell'API Google Play Developer. Tutte le versioni appena aggiunte nella release sono considerate della 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 dell'API Google Play Developer.
La priorità dell'aggiornamento in-app deve essere specificata nella risorsa
Edit.tracks
passata nel metodo
Edit.tracks: update
. L'esempio seguente mostra il rilascio di un'app con 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 prende in considerazione il valore inAppUpdatePriority
per tutti i codici versione dell'app tra la versione installata e la versione più recente disponibile, indipendentemente dal canale di rilascio. Considera ad esempio il seguente scenario:
- Carichi la versione 1 in un canale di produzione senza priorità.
- Pubblichi la versione 2 in un canale di test interno con priorità 5.
- Carichi la versione 3 in un canale di produzione senza priorità.
Quando gli utenti di produzione eseguono l'aggiornamento dalla versione 1 alla versione 3, ottengono 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. } });
Avviare un aggiornamento
Dopo aver verificato 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 di nuovo
che l'aggiornamento sia disponibile e consentito.
Puoi registrare un comando di avvio dei risultati dell'attività utilizzando il contratto predefinito
ActivityResultContracts.StartIntentSenderForResult
. Consulta la sezione su come ricevere una chiamata per l'aggiornamento dello stato.
I passaggi successivi dipendono dal fatto che tu stia richiedendo un aggiornamento flessibile o un aggiornamento immediato.
Configurare un aggiornamento con AppUpdateOptions
AppUpdateOptions
contiene un campo AllowAssetPackDeletion
che definisce se l'aggiornamento è autorizzato a eliminare i pacchetti di asset in caso di spazio di archiviazione del dispositivo limitato. Per impostazione predefinita, questo campo è impostato su false
, 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 chiamata per l'aggiornamento dello stato
Dopo aver avviato un aggiornamento, il callback del programma di avvio dei risultati dell'attività registrato 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 dal callback onActivityResult()
:
RESULT_OK
: l'utente ha accettato l'aggiornamento. Per gli aggiornamenti immediati, potresti non ricevere questo callback perché l'aggiornamento dovrebbe essere già terminato quando il controllo viene restituito alla tua app.RESULT_CANCELED
: l'utente ha negato o annullato l'aggiornamento.ActivityResult.RESULT_IN_APP_UPDATE_FAILED
: un altro errore ha impedito all'utente di fornire il consenso o all'aggiornamento di procedere.
Gestire un aggiornamento flessibile
Quando avvii un aggiornamento flessibile, viene visualizzata una finestra di dialogo per richiedere il consenso dell'utente. Se l'utente acconsente, il download viene avviato 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.
Monitorare lo stato di aggiornamento flessibile
Dopo l'inizio del download di un aggiornamento flessibile, l'app deve monitorare lo stato dell'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 ascoltatore per gli aggiornamenti dello 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);
Installare 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 finché non decide di installare l'aggiornamento.
Ti consigliamo di fornire una notifica (o un'altra indicazione dell'interfaccia utente) per informare l'utente che l'aggiornamento è pronto per l'installazione e richiedere conferma prima di riavviare l'app.
L'esempio seguente mostra l'implementazione di una barra di scorrimento di Material Design che richiede all'utente di confermare il 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'interfaccia utente a schermo intero che riavvia l'app
in background. Dopo che la piattaforma ha installato l'aggiornamento, l'app si riavvia nella sua attività principale.
Se invece chiami completeUpdate()
quando l'app è in background, l'aggiornamento viene installato in silenzio
senza oscurare l'interfaccia utente del dispositivo.
Ogni volta che l'utente porta la tua app in primo piano, controlla se è presente un aggiornamento in attesa di installazione. Se la tua app ha un aggiornamento nello stato DOWNLOADED
, chiedi all'utente di installarlo. In caso contrario, i dati di aggiornamento
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(); } }); }
Gestire un aggiornamento immediato
Quando avvii un aggiornamento immediato e l'utente acconsente all'avvio, Google Play mostra l'avanzamento dell'aggiornamento sopra l'interfaccia utente dell'app per tutta la durata dell'aggiornamento. Se l'utente chiude o termina l'app durante l'aggiornamento, l'aggiornamento dovrebbe continuare a essere scaricato e installato in background senza ulteriore conferma da parte dell'utente.
Tuttavia, quando l'app torna in primo piano, devi verificare che l'aggiornamento non sia in stato di blocco in UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS
. Se l'aggiornamento è in stallo 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 di startUpdateFlowForResult(). In particolare, la tua app deve 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. L'app dovrebbe determinare il modo migliore per procedere.
Se possibile, lascia che l'utente continui senza l'aggiornamento e riprova più tardi. Se la tua app non può funzionare senza l'aggiornamento, ti consigliamo di mostrare un messaggio informativo prima di riavviare il flusso di aggiornamento o di chiedere all'utente di chiudere l'app. In questo modo, l'utente saprà che può riavviare l'app quando è pronto a installare l'aggiornamento richiesto.
Passaggi successivi
Esegui il test degli aggiornamenti in-app della tua app per verificare che l'integrazione funzioni correttamente.