Este guia descreve como oferecer compatibilidade com atualizações no app usando Kotlin ou Java. Há guias separados para casos em que a implementação usa o código nativo (C/C++) e os casos em que ela usa o Unity.
Configurar seu ambiente de desenvolvimento
A biblioteca Play In-App Update faz parte das Bibliotecas Google Play Core. Inclua a dependência do Gradle abaixo para integrar a biblioteca Play In-App Update do 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") ... }
Conferir se há atualizações disponíveis
Antes de solicitar uma atualização, confira se há uma disponível
para seu app,
usando
AppUpdateManager
:
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. } });
A instância
AppUpdateInfo
retornada contém o status de disponibilidade da atualização. Dependendo do status da
atualização, ela também contém o seguinte:
- Se uma atualização estiver disponível e ela for permitida, a instância também terá uma intent para iniciá-la.
- Se uma atualização no app já estiver em andamento, a instância também vai informar o status dela.
Conferir inatividade de atualização
Além de conferir se uma atualização está disponível, também é possível saber quanto tempo se passou desde que o usuário foi notificado pela última vez sobre uma atualização pela Play Store. Isso pode ajudar você a decidir se deve iniciar uma atualização flexível ou uma imediata. Por exemplo, você pode esperar alguns dias antes de notificar o usuário sobre uma atualização flexível e mais alguns antes de exigir uma imediata.
Use
clientVersionStalenessDays()
para verificar o número de dias desde que a atualização foi disponibilizada na 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. } });
Verificar prioridade de atualização
A API Google Play Developer permite que você defina a prioridade de cada atualização. Isso permite que o app decida como recomendar uma atualização para o usuário. Por exemplo, considere a seguinte estratégia para definir a prioridade de atualização:
- Pequenas melhorias na IU: atualização de baixa prioridade. Não exigem a atualização flexível nem a imediata. Só atualize quando o usuário não estiver interagindo com o app.
- Melhorias de performance: atualização de prioridade média. Exigem uma atualização flexível.
- Atualização crítica de segurança: é de alta prioridade e precisa ser feita de forma imediata.
Para determinar a prioridade, o Google Play usa um valor inteiro entre 0 e 5, sendo 0
o padrão e 5 a prioridade mais alta. Para definir a prioridade de uma
atualização, use o campo inAppUpdatePriority
em Edits.tracks.releases
na
API Google Play Developer. Todas as versões recém-adicionadas são
consideradas como tendo a mesma prioridade da versão lançada. A prioridade só pode ser definida ao lançar
uma nova versão e não pode ser mudada posteriormente.
Defina a prioridade usando a API Google Play Developer, conforme descrito na documentação
da API Google Play
Developer.
A prioridade de atualização no app precisa ser especificada no recurso
Edit.tracks
transmitido no
método
Edit.tracks: update
. O exemplo a seguir demonstra o lançamento de um app com o código de versão 88
e inAppUpdatePriority
5:
{ "releases": [{ "versionCodes": ["88"], "inAppUpdatePriority": 5, "status": "completed" }] }
No código do app, é possível conferir o nível de prioridade de uma determinada atualização usando o método
updatePriority()
.
A prioridade retornada considera a inAppUpdatePriority
para todos os códigos
de versão do app entre a versão instalada e a mais recente disponível,
independente da faixa de lançamento. Por exemplo, considere este cenário:
- Você lançou a versão 1 em uma faixa de produção sem prioridade.
- Você lançou a versão 2 em uma faixa de teste interno com prioridade 5.
- Você lançou a versão 3 em uma faixa de produção sem prioridade.
Quando os usuários da versão de produção são atualizados da versão 1 para a 3, eles recebem a prioridade 5, mesmo que a versão 2 tenha sido publicada em uma faixa diferente.
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. } });
Iniciar uma atualização
Depois de confirmar que há uma atualização disponível, solicite-a atualização usando
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());
Cada instância AppUpdateInfo
só pode ser usada uma vez para iniciar uma atualização. Para repetir
a atualização em caso de falha, solicite uma nova AppUpdateInfo
e confira novamente
se a atualização está disponível e é permitida.
Você pode registrar um inicializador de resultados de atividades usando o contrato
ActivityResultContracts.StartIntentSenderForResult
integrado. Confira a seção sobre
como receber o status de atualização do callback.
As próximas etapas dependem do tipo de atualização que você está solicitando: flexível ou imediata.
Configurar uma atualização com AppUpdateOptions
AppUpdateOptions
contém um campo AllowAssetPackDeletion
que define se a atualização pode
limpar os pacotes de recursos caso o
armazenamento do dispositivo esteja limitado. Esse campo é definido como false
por padrão, mas é possível usar
o método setAllowAssetPackDeletion()
para defini-lo como 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());
Receber um callback para o status da atualização
Depois de iniciar uma atualização, o callback do inicializador de resultados de atividades registrado recebe o resultado da caixa de diálogo de confirmação:
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. } } });
Você pode receber diferentes valores do callback
onActivityResult()
:
RESULT_OK
: o usuário aceitou a atualização. No caso de atualizações imediatas, talvez você não receba esse callback, já que a atualização provavelmente já terá sido concluída quando o controle de tempo for devolvido ao app.RESULT_CANCELED
: o usuário negou ou cancelou a atualização.ActivityResult.RESULT_IN_APP_UPDATE_FAILED
: outro erro impediu que o usuário permitisse a atualização ou que ela continuasse.
Processar uma atualização flexível
Quando você inicia uma atualização flexível, uma caixa de diálogo aparece para solicitar o consentimento do usuário. Se ele consentir, o download será iniciado em segundo plano e o usuário poderá continuar interagindo com o aplicativo. Esta seção descreve como monitorar e concluir uma atualização flexível no app.
Monitorar o estado da atualização flexível
Depois que o download é iniciado para uma atualização flexível, seu app precisa monitorar o estado da atualização para saber quando ela pode ser instalada e para exibir o progresso na IU do app.
Você pode monitorar o estado de uma atualização em andamento registrando um listener para instalar as atualizações de status. Você também pode fornecer uma barra de progresso na interface do app para informar os usuários sobre o progresso do 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);
Instalar uma atualização flexível
Quando você detecta o estado InstallStatus.DOWNLOADED
, é necessário reiniciar o
app para instalar a atualização.
Ao contrário das atualizações imediatas, o Google Play não aciona automaticamente a reinicialização de um app para uma atualização flexível. Isso ocorre porque, durante uma atualização flexível, o usuário pretende continuar interagindo com o app até decidir que quer instalar a atualização.
Recomendamos que você apresente uma notificação ou alguma outra indicação na IU que informe ao usuário que a atualização está pronta para ser instalada e solicite a confirmação para reiniciar o app.
O exemplo a seguir demonstra a implementação de uma snackbar com Material Design (link em inglês) que solicita a confirmação do usuário para reiniciar o 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 você chama o
appUpdateManager.completeUpdate()
em primeiro plano, a plataforma exibe uma IU em tela cheia que reinicia o app
em segundo. Depois que a plataforma instala a atualização, o app é reiniciado para
a atividade principal.
Se, em vez disso, você chama completeUpdate()
quando o app estiver em
segundo plano, a atualização é instalada silenciosamente,
sem ocultar a interface do dispositivo.
Sempre que o usuário colocar seu app em primeiro plano, confira se há alguma
atualização esperando para ser instalada. Se o app tiver uma atualização no estado DOWNLOADED
,
solicite que o usuário a instale. Caso contrário, os dados de atualização continuarão
ocupando o armazenamento do dispositivo.
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(); } }); }
Gerenciar uma atualização imediata
Quando você inicia uma atualização imediata e o usuário consente em começá-la, o Google Play exibe o progresso da atualização na parte superior da IU do app durante todo o processo. Se o usuário fechar ou encerrar o app durante a atualização, o download e a instalação dela continuarão em segundo plano, sem necessidade de outra confirmação.
No entanto, quando o app retornar para o primeiro plano, você precisará confirmar se a
atualização não está parada no
estado
UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS
. Se estiver, retome a atualização:
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()); } }); }
O fluxo de atualização retorna um resultado, conforme descrito na documentação de referência para startUpdateFlowForResult(). Em particular, seu app precisa conseguir lidar com casos em que um usuário recusa a atualização ou cancela o download. Quando o usuário realiza uma dessas ações, a IU do Google Play é fechada. Seu app precisa determinar a melhor maneira de prosseguir.
Se possível, permita que o usuário continue sem a atualização e solicite-a novamente mais tarde. Caso o app não funcione sem a atualização, recomendamos exibir uma mensagem informativa antes de reiniciar o fluxo de atualização ou solicitar que o usuário feche o app. Dessa forma, o usuário entenderá que pode reiniciar o app quando estiver pronto para instalar a atualização necessária.
Próximas etapas
Testar as atualizações no app para verificar se a integração está funcionando corretamente.