ANRs

Quando a linha de execução de interface de um app Android é bloqueada por muito tempo, o erro "O app não está respondendo" (ANR) é acionado. Se o app estiver em primeiro plano, o sistema vai mostrar uma caixa de diálogo ao usuário, como na figura 1. A caixa de diálogo ANR dá ao usuário a oportunidade de forçar o fechamento do app.

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

ANRs são um problema porque fazem com que a linha de execução principal do app, que é responsável por atualizar a IU, não processe eventos de entrada do usuário ou não seja renderizada, causando frustração ao usuário. Para mais informações sobre a linha de execução principal do app, consulte Visão geral de processos e linhas de execução.

Um ANR é acionado para o app quando uma das seguintes condições ocorre:

  • Tempo de entrega de entradas esgotado:se o app não respondeu a um evento de entrada (como pressionamento de tecla ou toque na tela) em até cinco segundos.
  • Serviço executado:se um serviço declarado pelo app não consegue terminar a execução de Service.onCreate e Service.onStartCommand/Service.onBind em alguns segundos.
  • Service.startForeground não chamado: Se o app usa Context.startForegroundService para iniciar um novo serviço em primeiro plano mas o serviço não chama startForeground dentro de cinco segundos.
  • Transmissão de intent: se uma BroadcastReceiver não termina de ser executada dentro de um período definido. Se o app tem alguma atividade em primeiro plano, esse tempo limite é de cinco segundos.
  • JobScheduler interações: se um JobService não retornar de JobService.onStartJob ou JobService.onStopJob em alguns segundos, ou se um job iniciado pelo usuário começar e seu app não chamar JobService.setNotification alguns segundos depois de JobService.onStartJob ser chamado. Em apps destinados ao Android 13 e versões anteriores, os ANRs são silenciosos e não são informados ao app. Para apps direcionados ao Android 14 e versões mais recentes, os ANRs são explícitos e informados ao app.

Se o app estiver apresentando ANRs, use as orientações deste documento para diagnosticar e corrigir o problema.

Detectar o problema

Se você já publicou seu app, use o Android vitals para ver informações sobre os ANRs dele. É possível usar outras ferramentas para detectar ANRs, mas as ferramentas de terceiros não os informa em versões mais antigas do Android (Android 10 e anteriores), ao contrário do Android vitals.

Android vitals

O Android vitals pode ajudar a monitorar e melhorar a taxa de ANRs do seu app. Ele mede várias taxas de ANR:

  • Taxa de ANR:é a porcentagem de usuários ativos por dia que tiveram qualquer tipo de ANR.
  • Taxa de ANR percebido pelo usuário:é a porcentagem de usuários ativos por dia que perceberam pelo menos um ANR percebido pelo usuário. Atualmente, apenas ANRs do tipo Input dispatching timed out são considerados percebidos pelo usuário.
  • Taxa de ANRs múltiplos:é a porcentagem de usuários ativos por dia que tiveram pelo menos dois ANRs.

Um usuário ativo por dia é um usuário único que usa seu app em um único dia em um único dispositivo, possivelmente em várias sessões. Se o usuário utilizar o app em mais de um dispositivo em um único dia, cada dispositivo vai contribuir separadamente para o número de usuários ativos nesse dia.

A taxa de ANR percebido pelo usuário é uma das principais métricas, ou seja, ela afeta a possibilidade de descoberta do app no Google Play. Isso é importante porque os ANRs que ela conta sempre ocorrem quando o usuário está engajado com o app, o que causa mais interrupções.

O Google Play definiu dois limites de mau comportamento nessa métrica:

  • Limite de mau comportamento geral:pelo menos 0, 47% dos usuários ativos por dia percebem um ANR percebido pelo usuário em todos os modelos de dispositivos.
  • Limite de mau comportamento por dispositivo:pelo menos 8% dos usuários ativos por dia percebem um ANR percebido pelo usuário em um único modelo de dispositivo.

Se o app exceder o limite de mau comportamento geral, ele provavelmente será mostrado para menos pessoas em todos os dispositivos. Se o app excede o limite de mau comportamento por dispositivo em alguns modelos, ele provavelmente é mostrado para menos pessoas que usam esse tipo de dispositivo, e um aviso é mostrado na página "Detalhes do app".

O Android vitals pode enviar alertas pelo Play Console quando o app está apresentando muitos ANRs.

Para informações sobre como o Google Play coleta dados do Android vitals, consulte a Play Console documentação.

Diagnosticar ANRs

Há alguns padrões comuns que precisam ser observados ao diagnosticar ANRs:

  • O app está fazendo operações lentas envolvendo E/S na linha de execução principal.
  • O app está fazendo um cálculo longo na linha de execução principal.
  • A linha de execução principal está fazendo uma chamada de vinculação síncrona para outro processo, e esse outro processo está demorando muito para retornar.
  • A linha de execução principal está bloqueada e esperando um bloco sincronizado para uma operação longa que está acontecendo em outra linha de execução.
  • A linha de execução principal está em um impasse com outra linha, seja em um processo ou em uma chamada de vinculação. A linha de execução principal não está apenas aguardando uma operação longa terminar, mas está em uma situação de impasse.

As técnicas a seguir podem ajudar a determinar a causa dos ANRs.

HealthStats

HealthStats fornece métricas sobre a integridade do app, coletando informações sobre tempo total de uso do usuário e do sistema, tempo de CPU, rede, estatísticas de rádio, tempo com a tela ativada e desativada e alarmes de ativação. Isso pode ajudar a medir o uso geral de CPU e o consumo de bateria.

Depuração

Debug ajuda a inspecionar aplicativos Android durante o desenvolvimento, fazendo uso de rastreamento e contagens de alocação para identificar instabilidade e pontos de atraso nos apps. Também é possível usar Debug para receber contadores relacionados à memória nativa e ao tempo de execução, além de métricas de memória que podem ajudar a identificar o consumo de memória por um processo específico.

ApplicationExitInfo

ApplicationExitInfo está disponível no Android 11 (nível 30 da API) e versões mais recentes e fornece informações sobre o motivo do encerramento do app. Os motivos incluem ANRs, pouca memória, falhas no app, uso excessivo da CPU, interrupções do usuário, interrupções do sistema e mudanças na permissão de execução.

Modo restrito

Usar StrictMode ajuda a encontrar operações acidentais de E/S na linha de execução principal durante o desenvolvimento do app. Você pode usar StrictMode no nível do aplicativo ou da atividade.

Ativar caixas de diálogo ANR em segundo plano

O Android mostra caixas de diálogo ANR para apps que levam muito tempo para processar a mensagem de transmissão apenas se você ativar Mostrar todos os ANRs nas Opções do desenvolvedor do dispositivo. Por esse motivo, as caixas de diálogo ANR em segundo plano nem sempre são mostradas para o usuário, mesmo quando o app está apresentando problemas de desempenho.

Gargalos de recomposição

Use o Android Studio Profiler e o Layout Inspector para rastrear gargalos de recomposição. Para mais informações, consulte Jetpack Compose Performance.

Extrair um arquivo de rastreamento

O Android armazena informações de rastros quando passa por um ANR. Em versões mais antigas do SO, há um único arquivo /data/anr/traces.txt no dispositivo. Nas versões mais recentes do SO, há vários arquivos /data/anr/anr_*. Você pode acessar os rastros de ANR em um dispositivo ou emulador usando o Android Debug Bridge (adb) como raiz:

adb root
adb shell ls /data/anr
adb pull /data/anr/<filename>

Você pode capturar um relatório de bug de um dispositivo físico usando a opção de desenvolvedor "Criar relatório do problema" no dispositivo ou o comando adb bugreport na máquina de desenvolvimento. Para mais informações, consulte Capturar e ler relatórios de bugs.

Corrigir os problemas

Depois de identificar o problema, você pode usar as dicas desta seção para corrigir problemas encontrados com frequência.

Código lento na linha de execução principal

Identifique os lugares no código em que a linha de execução principal do app está ocupada por mais de cinco segundos. Procure os casos de uso suspeitos no app e tente reproduzir o ANR.

Um problema comum é uma tarefa de longa duração diretamente em um elemento combinável:

@Composable
fun BadList(rawStrings: List<String>) {
    // Math or sorting inside the composable runs on EVERY recomposition pass!
    val heavilyProcessedList = rawStrings
        .filter { it.isNotBlank() }
        .map { it.uppercase().reversed() }
        .map { it.computationallyHeavyFunction() }
.sortedBy { it.length }
    LazyColumn { items(sortedList) { Text(it) } }
}

// Modern Compose-first fix
@Composable
fun GoodList(viewModel: MyViewModel = viewModel()) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()

    // UI simply renders state; no heavy processing allowed here
    LazyColumn { items(uiState.sortedData) { Text(it) } }
}

E/S na linha de execução principal

A execução de operações de E/S na linha de execução principal é uma causa comum de operações lentas, o que pode causar ANRs. No Compose, os desenvolvedores costumam acionar acidentalmente leituras de disco (como SharedPreferences ou chamadas de banco de dados) ao tentar derivar o estado inicial.

Execute operações de E/S de longa duração fora da camada de interface. Use withContext(Dispatchers.IO) em uma ViewModel ou, ainda melhor, use um Repository na camada de dados.

Impasses

Um impasse ocorre quando uma linha de execução entra em um estado de espera porque um recurso necessário é retido por outra linha, que também está aguardando um recurso retido pela primeira. Se a linha de execução principal do app estiver nessa situação, é provável que ANRs aconteçam.

Os impasses são um fenômeno bem estudado na ciência da computação e existem algoritmos de prevenção que você pode usar para os evitar.

Para mais informações, consulte Impasse e Algoritmos de prevenção de impasse na Wikipedia.

Ao usar o Kotlin e o Compose, você pode substituir bloqueios primitivos por mutexes de corrotina não bloqueadores (Mutex.withLock) para evitar o bloqueio de linhas de execução suspendendo o contexto de execução em vez de congelar a linha de execução de interface. Exemplo:

import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

// Modern non-blocking concurrency state architecture
class SecureDataRepository {
    private val mutex = Mutex()

    suspend fun safeUIAccess() {
        // If locked, the main thread suspends seamlessly, preventing an ANR
        mutex.withLock {
            performSafeOperation()
        }
    }
}

Broadcast receivers lentos

Os apps podem responder a mensagens de transmissão, como ativar ou desativar o modo avião ou mudar o status de conectividade usando broadcast receivers. Um ANR ocorre quando um app demora muito para processar a mensagem de transmissão.

Um ANR ocorre nestes casos:

  • Após um período considerável, um broadcast receiver ainda não concluiu a execução do método onReceive.
  • Um broadcast receiver chama goAsync e falha ao chamar finish no objeto PendingResult.

O app só pode executar operações curtas no onReceive método de um BroadcastReceiver. No entanto, se o app exigir processamento mais complexo como resultado de uma mensagem de transmissão, você vai precisar adiar a tarefa para uma ViewModel (aproveitando o poder das corrotinas, escopos e agentes do Kotlin) se a tarefa levar alguns segundos no máximo, qualquer tipo de detentor de estado, ou para WorkManager para tarefas que devem levar mais de alguns segundos.

GameActivity

A biblioteca GameActivity reduziu os ANRs em estudos de caso de jogos e apps escritos em C ou C++. Se você substituir a atividade nativa existente por GameActivity, poderá reduzir o bloqueio de linhas de execução de interface e impedir que alguns ANRs ocorram.

Para mais informações sobre ANRs, consulte Como manter seu app responsivo. Para mais informações sobre linhas de execução, consulte Melhor desempenho com linhas de execução.

Outros recursos

Visualizar conteúdo