Encontrar a linha de execução que não responde

Este documento mostra como identificar a linha de execução que não responde em um despejo de pilha ANR. A linha de execução não responsiva varia de acordo com o tipo de ANR, conforme mostrado na tabela abaixo.

Tipo de ANR Linha de execução que não responde
Envio de entrada Linha de execução principal
Envio de entrada sem janela em foco Linha de execução principal Esse tipo de ANR geralmente não é causado por uma linha de execução bloqueada.
Broadcast receiver (síncrono) Linha de execução ativa em onReceive(). Essa é a linha de execução principal, a menos que um gerenciador personalizado em uma linha de execução que não seja a principal seja especificado usando Context.registerReceiver.
Broadcast receiver (assíncrono) Verifique o código para saber qual linha de execução ou pool de linhas de execução é responsável por fazer o trabalho de processar a transmissão depois que goAsync é chamado.
Tempo limite atingido do serviço em execução Linha de execução principal
Início de serviço em primeiro plano Linha de execução principal
O provedor de conteúdo não está respondendo Possibilidades:
  • A linha de execução de vinculação se o ANR for causado por uma consulta lenta do provedor de conteúdo.
  • Linha de execução principal se o ANR for causado por uma inicialização longa do app.
Sem resposta para onStartJob ou onStopJob Linha de execução principal

Às vezes, a linha de execução não responde devido a uma causa raiz em uma linha de execução ou processo diferente. A linha de execução pode deixar de responder devido à espera pelo seguinte:

  • Um bloqueio mantido por uma linha de execução diferente.
  • Uma chamada de vinculação lenta para um processo diferente.

Causas comuns de linhas de execução que não respondem

Confira abaixo causas comuns de linhas de execução que não respondem.

Chamada de vinculação lenta

Embora a maioria das chamadas de vinculação seja rápida, a cauda longa pode ser muito lenta. É mais provável que isso aconteça se o dispositivo estiver carregado ou se a linha de execução de resposta da vinculação for lenta, como na contenção de bloqueio, em muitas chamadas de vinculação recebidas ou no tempo limite da camada de abstração de hardware (HAL).

Você pode resolver isso movendo chamadas de vinculação síncronas para linhas de execução em segundo plano sempre que possível. Se a chamada precisar acontecer na linha de execução principal, descubra por que ela está lenta. A melhor maneira de fazer isso é usando rastros do Perfetto.

Procure por BinderProxy.transactNative ou Binderproxy.transact nas pilhas. Isso significa que uma chamada de vinculação está ocorrendo. Seguindo essas duas linhas, é possível notar a API de vinculação que é chamada. No exemplo abaixo, a chamada é para IAccessibilityManager.addClient.

main tid=123

...
android.os.BinderProxy.transactNative (Native method)
android.os.BinderProxy.transact (BinderProxy.java:568)
android.view.accessibility.IAccessibilityManager$Stub$Proxy.addClient (IAccessibilityManager.java:599)
...

Muitas chamadas de vinculação consecutivas

A execução de várias chamadas de vinculação consecutivas em um loop restrito pode bloquear uma linha de execução por um longo período.

Uma E/S com bloqueio

Nunca realize E/S de bloqueio na linha de execução principal. Esse é um antipadrão.

Contenção de bloqueio

Se uma linha de execução for bloqueada ao adquirir um bloqueio, isso poderá resultar em um ANR.

O exemplo abaixo mostra que a linha de execução principal está bloqueada ao tentar adquirir um bloqueio:

main (tid=1) Blocked

Waiting for com.example.android.apps.foo.BarCache (0x07d657b7) held by
ptz-rcs-28-EDITOR_REMOTE_VIDEO_DOWNLOAD
[...]
at android.app.ActivityThread.handleStopActivity(ActivityThread.java:5412)
[...]

A linha de execução de bloqueio está fazendo uma solicitação HTTP para fazer o download de um vídeo:

ptz-rcs-28-EDITOR_REMOTE_VIDEO_DOWNLOAD (tid=110) Waiting

at jdk.internal.misc.Unsafe.park(Native method:0)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:211)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:715)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1047)
at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:230)
at com.example.android.apps.foo.HttpRequest.execute(HttpRequest:136)
at com.example.android.apps.foo$Task$VideoLoadTask.downloadVideoToFile(RequestExecutor:711)
[...]

Frame caro

Renderizar muitas coisas em um único frame pode fazer com que a linha de execução principal não responda pela duração do frame, como neste exemplo:

  • Renderizar muitos itens desnecessários fora da tela.
  • Usar um algoritmo ineficiente, como O(n^2), ao renderizar muitos elementos da interface.

Bloqueada por outro componente

Se outro componente, como um broadcast receiver, bloquear a linha de execução principal por mais de cinco segundos, isso pode causar ANRs de envio de entrada e uma instabilidade grave.

Evite fazer qualquer trabalho pesado na linha de execução principal dos componentes do app. Execute broadcast receivers em uma linha de execução diferente sempre que possível.