Manter seu app responsivo

Figura 1. Uma caixa de diálogo de ANR mostrada ao usuário.

Este documento descreve como o sistema Android determina se um app deixou de responder e oferece diretrizes para garantir que ele se mantenha responsivo.

Mesmo que o código seja bem escrito, é possível que o app ainda fique lento, trave, congele por períodos significativos ou demore muito para processar entradas. Se o app estiver em primeiro plano e não responder, uma caixa de diálogo "O app não está respondendo" (ANR) vai aparecer, conforme mostrado na Figura 1. A caixa de diálogo ANR permite que o usuário force o fechamento do app. Se o app não estiver em primeiro plano, ele será parado silenciosamente. É fundamental projetar a capacidade de resposta no app para minimizar as caixas de diálogo ANR.

Acionadores de ANR

Geralmente, o sistema mostra um ANR quando um app não pode responder à entrada do usuário na linha de execução principal, também conhecida como linha de execução de interface, impedindo que o sistema processe os eventos de entrada do usuário recebidos.

Por exemplo, um ANR pode ocorrer se um app realizar uma operação de E/S de bloqueio, como acesso à rede, na linha de execução de interface. Outro exemplo é quando um app passa muito tempo criando uma estrutura elaborada na memória ou calculando a próxima jogada em um jogo na linha de execução de interface.

No Android, a capacidade de resposta do app é monitorada pelos serviços de sistema ActivityManager e WindowManager. O Android mostra a caixa de diálogo ANR para um app quando detecta uma das condições abaixo:

  • Nenhuma resposta a um evento de entrada (por exemplo, eventos de pressionamento de tecla ou toque na tela) em até cinco segundos.
  • Um BroadcastReceiver não termina a execução em 10 a 20 segundos para intents em primeiro plano. Para mais informações, consulte Tempo limite do Broadcast receiver.

Evitar ANRs

Confira abaixo dicas gerais para evitar ANRs. Para saber mais sobre como diagnosticar e depurar diferentes tipos de ANR, consulte as outras páginas desta seção.

  • Mantenha a linha de execução principal sempre desbloqueada e use as linhas de execução de forma estratégica.

    • Não execute operações de bloqueio ou de longa duração na linha de execução principal do app. Em vez disso, crie uma linha de execução de worker e faça a maior parte do trabalho nela.

    • Tente minimizar todas as contenções de bloqueio entre a linha de execução principal e outras linhas de execução.

    • Minimize qualquer trabalho não relacionado à interface na linha de execução principal, por exemplo, ao processar transmissões ou executar serviços. Todo método realizado na linha de execução de interface precisa fazer o mínimo de trabalho possível nessa linha de execução. Em especial, as atividades precisam fazer o mínimo possível para configurar os principais métodos do ciclo de vida, por exemplo, onCreate() e onResume(). Consulte a Visão geral dos trabalhos em segundo plano para mais informações sobre as soluções disponíveis para programar o trabalho em uma linha de execução em segundo plano e se comunicar com a interface.

    • Tenha cuidado ao compartilhar pools de linhas de execução entre componentes. Não use as mesmas linhas de execução para operações de bloqueio possivelmente longas e tarefas urgentes, como recebimento de transmissão.

    .
  • Agilize a inicialização do app. Minimize operações lentas ou de bloqueio no código de inicialização do app, por exemplo, métodos executados durante a inicialização do Dagger.

  • Se você estiver usando BroadcastReceiver, execute broadcast receivers em uma linha de execução não principal com Context.registerReceiver. Para mais informações, consulte ANRs em broadcast receivers.

ANRs em broadcast receivers

O tempo de execução de BroadcastReceiver é limitado porque o objetivo dos broadcast receivers é realizar pequenas quantidades de trabalho discretas em segundo plano, por exemplo, salvar uma configuração ou registrar uma Notification de dois minutos. Assim como com outros métodos chamados na linha de execução de interface, os apps precisam evitar operações ou cálculos possivelmente longos em broadcast receivers. Em vez de realizar tarefas de longa duração na linha de execução de interface, execute-as em segundo plano para execução futura. Consulte Visão geral dos trabalhos em segundo plano para mais informações sobre possíveis soluções.

Outro problema comum com objetos BroadcastReceiver ocorre quando eles são executados com muita frequência. A execução frequente em segundo plano pode reduzir a quantidade de memória disponível para outros apps. Para mais informações sobre como ativar e desativar objetos BroadcastReceiver de forma eficiente, consulte Visão geral de transmissões.

Reforçar a capacidade de resposta

Geralmente, 100 a 200 ms é o limite além do qual os usuários percebem lentidão em um app. Confira abaixo mais dicas de como fazer seu app parecer responsivo para os usuários.

  • Caso seu app esteja executando algo em segundo plano em resposta a uma entrada do usuário, use uma ProgressBar para mostrar o progresso da execução na interface.

  • Para jogos especificamente, faça cálculos de movimentos em uma linha de execução de worker.

  • Caso seu app tenha uma fase de configuração inicial demorada, considere mostrar uma tela de apresentação ou renderizar a visualização principal o mais rápido possível. Indique que o carregamento está em andamento e preencha as informações de forma assíncrona. Nos dois casos, recomendamos indicar de alguma forma que o progresso está sendo feito, para que o usuário não perceba que o app está congelado.

  • Use ferramentas de análise de performance, por exemplo, o Perfetto e o CPU Profiler, para determinar gargalos na capacidade de resposta do app.