Participe do evento ⁠#Android11: apresentação de lançamento da versão Beta no dia 3 de junho.

Compatibilidade com atualizações no app

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 posteriores e exigem que você use a Biblioteca Play Core 1.5.0 ou uma versão posterior. 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, você precisa verificar se ela está disponível para seu app. Para verificar se há atualizações, use o 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á um 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.

Iniciar uma atualização

Depois de verificar se você consegue atualizar o app, solicite uma atualização usando o AppUpdateManager.startUpdateFlowForResult(), conforme mostrado abaixo. 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, limite a solicitação de atualizações no app somente às mudanças importantes para a funcionalidade.

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, você pode usar um callback onActivityResult() para gerenciar uma falha ou um cancelamento de atualização, conforme mostrado abaixo.

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.

Kotlin

    // Create a listener to track request state updates.
    val listener = { state ->
        // Show module progress, 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 -> {
        // Show module progress, 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() 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 abaixo. 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);
                }
              });
    }
    

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:
    1. Feche completamente o app Google Play Store.
    2. Abra o app Google Play Store e acesse a guia Meus apps e jogos.
    3. Se o app que está sendo testado não aparecer com uma atualização disponível, verifique se você configurou corretamente as faixas de teste.