Manter o app atualizado nos dispositivos dos usuários permite que eles testem novos recursos e recebam melhorias de desempenho e correções de bugs. Embora alguns usuários ativem atualizações em segundo plano quando o dispositivo está em uma conexão ilimitada, pode ser necessário lembrar outros usuários de atualizar o app. As atualizações no app são um recurso da biblioteca Play Core que introduz um novo fluxo de solicitações para que os usuários ativos autorizem a atualização do app.
As atualizações no app só funcionam em dispositivos com Android 5.0 (API de nível 21) ou versões mais recentes e exigem que você use a biblioteca Play Core 1.5.0 ou uma versão mais recente. Além disso, as atualizações no app são compatíveis somente com apps executados em dispositivos móveis e tablets Android e com dispositivos Chrome OS.
Depois de atender a esses requisitos, seu app pode oferecer compatibilidade com a UX a seguir para recebimento de atualizações:
Flexível: uma experiência do usuário que fornece o download e a instalação em segundo plano com um monitoramento de estado elegante. Esta UX é apropriada quando é aceitável que o usuário use o app durante o download da atualização. Por exemplo, é recomendável incentivar os usuários a testar um novo recurso que não seja essencial para a funcionalidade principal do app.
Figura 1. Um exemplo de fluxo de atualização flexível.
Imediata: uma experiência de usuário em tela cheia que exige que o usuário atualize e reinicie o app para continuar a usá-lo. Essa UX é ideal para casos em que uma atualização é essencial para o uso contínuo do app. Depois que um usuário aceita uma atualização imediata, o Google Play cuida da instalação da atualização e reinicia o app.
Figura 2. Um exemplo de fluxo de atualização imediata.
Esta página mostra como usar a biblioteca Play Core para solicitar e executar uma atualização flexível ou imediata no app.
Verificar a disponibilidade de atualizações
Antes de solicitar uma atualização, verifique primeiro se há uma disponível
para seu app. Para verificar se há uma atualização, use
AppUpdateManager
,
conforme mostrado abaixo:
Kotlin
// Creates instance of the manager. 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 // For a flexible update, use AppUpdateType.FLEXIBLE && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) ) { // Request the update. } }
Java
// Creates instance of the manager. 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 // For a flexible update, use AppUpdateType.FLEXIBLE && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) { // Request the update. } });
O resultado contém o status de disponibilidade da atualização. Se uma atualização estiver disponível
e ela for permitida, a
AppUpdateInfo
retornada também conterá uma intent para iniciar a atualização. Consulte a seção a seguir para saber como
iniciar a atualização.
Se uma atualização no app já estiver em andamento, o resultado também informará o status da atualização em andamento.
Verificar inatividade de atualização
Além de verificar se uma atualização está disponível, também é possível verificar quanto tempo se passou desde que o usuário foi notificado de uma atualização pela Google Play Store. Isso pode ajudar você a decidir se o app deve iniciar uma atualização flexível ou imediata. Ou seja, você precisa esperar alguns dias antes de notificar o usuário com uma atualização flexível e mais alguns dias antes de exigir uma atualização imediata.
Para verificar o número de dias decorridos desde que a Google Play Store
descobriu uma atualização, use
clientVersionStalenessDays()
,
conforme mostrado abaixo:
Kotlin
// Creates instance of the manager. 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 { if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && appUpdateInfo.clientVersionStalenessDays() != null && appUpdateInfo.clientVersionStalenessDays() >= DAYS_FOR_FLEXIBLE_UPDATE && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) { // Request the update. }
Java
// Creates instance of the manager. 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 é uma atualização flexível ou imediata.
- Melhorias de desempenho: atualização de prioridade média. Solicita uma atualização flexível.
- Atualização crítica de segurança: atualização de alta prioridade. Exige uma atualização 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 na versão são
consideradas na prioridade da versão. A prioridade só pode ser definida ao lançar
uma nova versão e não pode ser alterada posteriormente.
Para definir a prioridade usando a API Google Play Developer, é possível criar um fluxo de trabalho
de edições, fazer upload de novos APKs ou pacotes, atribuí-los a uma faixa e confirmar
sua edição, conforme descrito na documentação da API Google Play Developer.
A prioridade de atualização no app precisa ser especificada no Edits.tracks resource
transmitido no
método Edits.tracks: update
. Por exemplo, para lançar um APK com o código de versão 88, com
inAppUpdatePriority
5:
{ "releases": [{ "versionCodes": ["88"], "inAppUpdatePriority": 5, "status": "completed" }] }
No código do app, você pode verificar o nível de prioridade de uma determinada atualização usando
updatePriority()
,
conforme mostrado abaixo:
Kotlin
// Creates instance of the manager. 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() >= HIGH_PRIORITY_UPDATE && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) { // Request an immediate update. }
Java
// Creates instance of the manager. 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() >= HIGH_PRIORITY_UPDATE && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) { // Request an immediate update. } });
Iniciar uma atualização
Depois de verificar se você consegue atualizar o app, é possível solicitar uma atualização
usando
AppUpdateManager.startUpdateFlowForResult()
,
conforme mostrado. No entanto, preste atenção à frequência com que solicita atualizações para
evitar que os usuários se incomodem ou se cansem. Ou seja, só peça
atualizações no app quando a funcionalidade estiver afetada.
Kotlin
appUpdateManager.startUpdateFlowForResult( // Pass the intent that is returned by 'getAppUpdateInfo()'. appUpdateInfo, // Or 'AppUpdateType.FLEXIBLE' for flexible updates. AppUpdateType.IMMEDIATE, // The current activity making the update request. this, // Include a request code to later monitor this update request. MY_REQUEST_CODE)
Java
appUpdateManager.startUpdateFlowForResult( // Pass the intent that is returned by 'getAppUpdateInfo()'. appUpdateInfo, // Or 'AppUpdateType.FLEXIBLE' for flexible updates. AppUpdateType.IMMEDIATE, // The current activity making the update request. this, // Include a request code to later monitor this update request. MY_REQUEST_CODE);
Cada instância AppUpdateInfo
só pode ser usada uma vez para iniciar uma atualização.
Para repetir a atualização em caso de falha, você precisa solicitar uma nova
AppUpdateInfo
e verificar novamente se a atualização está disponível e é permitida.
O tipo de atualização solicitada determina as próximas etapas necessárias. Para saber mais, leia a seção sobre como Processar uma atualização imediata ou Processar uma atualização flexível.
Receber um callback para o status da atualização
Depois de iniciar uma atualização, é possível usar um callback
onActivityResult()
para lidar com uma falha ou cancelamento de atualização, conforme mostrado.
Kotlin
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { if (requestCode == MY_REQUEST_CODE) { if (resultCode != RESULT_OK) { log("Update flow failed! Result code: $resultCode") // If the update is cancelled or fails, // you can request to start the update again. } } }
Java
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == MY_REQUEST_CODE) { if (resultCode != RESULT_OK) { log("Update flow failed! Result code: " + resultCode); // If the update is cancelled or fails, // you can request to start the update again. } } }
Veja a seguir os diferentes valores que você pode receber do
callback onActivityResult()
:
RESULT_OK
: o usuário aceitou a atualização. No caso de atualizações imediatas, você pode não receber este callback, já que a atualização provavelmente já terá sido concluída pelo Google Play quando o controle 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 a permissão do usuário. Se ele permitir, o download será iniciado em segundo plano, e o usuário poderá continuar interagindo com o app. 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 um usuário aceita uma atualização flexível, o Google Play começa a fazer o download dela em segundo plano. Depois disso, o app precisa monitorar o estado da atualização para saber quando ela poderá 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 IU do app para informar aos usuários até o final do download.
Kotlin
// Create a listener to track request state updates. val listener = { 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
Se você monitorar o estado da atualização flexível e detectar o
estado InstallStatus.DOWNLOADED
, será necessário reiniciar o app para instalar a
atualização.
Ao contrário de uma atualização imediata, o Google Play não acionará uma reinicialização do app para você. Isso acontece porque, durante uma atualização flexível, o usuário pretende continuar usando o app até decidir que quer instalar a atualização.
Por isso, recomendamos que você apresente uma notificação ou alguma outra indicação na IU que informe ao usuário de que a instalação está pronta e solicite a confirmação para reiniciar o app.
Por exemplo, você pode implementar uma snackbar com Material Design (link em inglês) solicitando a confirmação para reiniciar o app, como mostrado na Figura 1.
A amostra de código a seguir demonstra uma notificação snackbar que aparece para o usuário após o download de uma atualização flexível.
Kotlin
override fun onStateUpdate(state: InstallState) { 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
@Override public void onStateUpdate(InstallState 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 plano. Depois que a plataforma instalar a atualização, o app será reiniciado para
a atividade principal.
Se, em vez disso, você chamar appUpdateManager.completeUpdate()
quando o app estiver em
segundo plano, a atualização será instalada silenciosamente, sem ocultar a IU do dispositivo.
Quando o usuário coloca seu app em primeiro plano, é recomendável verificar
se ele não tem uma atualização esperando para ser instalada. Se o app tiver uma
atualização no estado DOWNLOADED
, mostre a notificação para solicitar que ele
instale a atualização, como mostrado. 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
Se você estiver realizando uma atualização imediata e o usuário consentir em instalar a atualização, o Google Play exibirá o progresso na parte superior da IU do app durante todo o processo. Durante a atualização, se o usuário fechar ou encerrar o app, o download e a instalação 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 ela estiver parada nesse estado, retome a atualização, conforme mostrado abaixo:
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, IMMEDIATE, this, MY_REQUEST_CODE ); } } }
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, IMMEDIATE, this, MY_REQUEST_CODE); } }); }
Seu app precisa conseguir processar 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, e 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 mais tarde para iniciar o fluxo de atualização novamente. Caso seu app não funcione sem a atualização, considere exibir uma mensagem informativa antes de reiniciar o fluxo de atualização ou solicitar que o usuário feche o app (ou feche o app). Dessa forma, o usuário entende que pode reiniciar seu app quando estiver pronto para fazer o download da atualização.
Faça testes com o Compartilhamento interno de apps
Com o Compartilhamento interno de apps, é possível compartilhar rapidamente um pacote de apps ou um APK com sua equipe e seus testadores internos fazendo o upload do pacote de apps que você quer testar no Play Console.
Você também pode usar o Compartilhamento interno de apps para testar atualizações no app da seguinte maneira:
- No dispositivo de teste, verifique se você já instalou uma versão do app
que atenda aos seguintes requisitos:
- o app foi instalado por um URL de compartilhamento interno de apps;
- compatível com atualizações no app;
- usa um código de versão anterior à versão atualizada do app.
- Siga as instruções do Play Console sobre como compartilhar seu app internamente. Certifique-se de fazer upload de uma versão do app que use um código de versão mais recente do que você já instalou no dispositivo de teste.
- No dispositivo de teste, clique no link de Compartilhamento interno do app somente para a versão atualizada dele. Não instale o app pela página da Google Play Store que aparece depois de clicar no link.
- Abra o app na gaveta de apps ou na tela inicial do dispositivo. Agora, a atualização está disponível e você pode testar a implementação de atualizações no app.
Resolver problemas
Esta seção descreve algumas soluções possíveis para situações em que as atualizações no app podem não funcionar conforme o esperado durante os testes.
- As atualizações no app estão disponíveis apenas para contas de usuário que possuem o app. Portanto, verifique se a conta que você está usando fez o download do app Google Play pelo menos uma vez antes de testar as atualizações.
- Verifique se o app com que você está testando as atualizações tem o mesmo ID do aplicativo e está assinado com a mesma chave de assinatura do app disponível no Google Play.
- Como o Google Play só pode atualizar um app para um código de versão posterior, verifique se o app que está sendo testado tem um código de versão anterior ao da atualização.
- Verifique se a conta está qualificada e se o cache do Google Play está
atualizado. Para fazer isso, com a conta da Google Play Store conectada no
dispositivo de teste, faça o seguinte:
- Feche completamente o app Google Play Store.
- Abra o app Google Play Store e acesse a guia Meus apps e jogos.
- Se o app que está sendo testado não aparecer com uma atualização disponível, verifique se você configurou as faixas de teste corretamente.