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 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 dos processos e das 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 um BroadcastReceiver não termina de ser executado dentro de um período definido. Se o app tem alguma atividade em primeiro plano, esse tempo limite é de cinco segundos.
  • Interações do JobScheduler: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 em 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.

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 no seu 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 de um aplicativo ao capturar o tempo total de uso do usuário e do sistema, tempo de CPU, rede, estatísticas de rádio, tempo com a tela ativada/desativada e alarmes de ativação. Isso pode ajudar você a medir o uso geral da CPU e o consumo de bateria.

Depuração

O Debug ajuda a inspecionar aplicativos Android durante o desenvolvimento, incluindo rastreamento e contagens de alocação para identificar instabilidade e 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) ou mais recente e fornece informações sobre o motivo do encerramento do app. Isso inclui 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á com problemas de desempenho.

Gargalos de recomposição

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

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 muitas vezes acionam 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 longe da camada de interface. Use withContext(Dispatchers.IO) em um ViewModel ou, melhor ainda, 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 Wikipédia.

Ao usar Kotlin e Compose, você pode substituir bloqueios primitivos por Mutexes de corrotina sem bloqueio (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 da 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 não chama finish no objeto PendingResult.

O app só pode executar operações curtas no método onReceive de um BroadcastReceiver. No entanto, se o app exigir um processamento mais complexo como resultado de uma mensagem de transmissão, adie a tarefa para um ViewModel (aproveitando o poder das corrotinas, escopos e distribuidores 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 atual 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 Manter seu app responsivo. Para mais informações sobre linhas de execução, consulte Melhor performance usando linhas de execução.

Outros recursos

Visualiza conteúdo