Rechercher le thread qui ne répond pas

Ce document explique comment identifier le thread qui ne répond pas dans une copie de pile ANR. Le thread qui ne répond pas varie selon le type d'ANR, comme indiqué dans le tableau suivant.

Type d'ANR Thread qui ne répond pas
Envoi des entrées Thread principal
Pas de fenêtre active pour l'envoi des entrées Thread principal. Ce type d'ANR n'est généralement pas engendré par un thread bloqué.
Broadcast receiver (synchrone) Thread exécutant onReceive(). Il s'agit du thread principal, sauf si un gestionnaire personnalisé sur un thread non principal est spécifié à l'aide de Context.registerReceiver.
Broadcast receiver (asynchrone) Vérifiez le code pour savoir quel thread ou pool de threads est responsable du traitement de la diffusion après l'appel de goAsync.
Exécution du délai avant expiration du service Thread principal
Démarrage du service de premier plan Thread principal
Le fournisseur de contenu ne répond pas Soit :
  • Thread de liaison si l'erreur ANR est provoquée par une requête lente du fournisseur de contenu.
  • Thread principal si l'erreur ANR est provoquée par un long démarrage de l'application.
Aucune réponse à onStartJob ou onStopJob Thread principal

Parfois, le thread ne répond pas à cause d'une erreur dans un autre thread ou processus. Le thread peut ne pas répondre si vous attendez les éléments suivants :

  • Verrou détenu par un thread différent.
  • Appel de liaison lent à un processus différent.

Causes courantes d'absence de réponse des threads

Les causes courantes d'absence de réponse des threads sont les suivantes.

Appel de liaison lent

Bien que la plupart des appels de liaison soient rapides, la longue traîne peut être très lente. Cela est plus probable de se produire si l'appareil est chargé ou si le thread de réponse de liaison est lent, par exemple en cas de conflit de verrouillage, d'un nombre élevé d'appels de liaison entrants ou de dépassement du délai d'expiration d'une couche d'abstraction matérielle (HAL).

Pour résoudre ce problème, déplacez les appels de liaison synchrones vers des threads en arrière-plan dans la mesure du possible. Si l'appel doit avoir lieu sur le thread principal, découvrez pourquoi il est lent. Pour ce faire, le meilleur moyen consiste à utiliser les traces Perfetto.

Recherchez BinderProxy.transactNative ou Binderproxy.transact dans les piles. Cela signifie qu'un appel de liaison est en cours. Après ces deux lignes, vous pouvez voir l'API de liaison appelée. Dans l'exemple suivant, l'appel est 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)
...

Nombreux appels de liaison consécutifs

Effectuer de nombreux appels de liaison consécutifs dans une boucle étroite peut bloquer un thread pendant une longue période.

Une E/S bloquante

N'exécutez jamais d'E/S bloquantes sur le thread principal. C’est un antimodèle.

Conflits de verrouillage

Si un thread est bloqué lors de l'acquisition d'un verrouillage, cela peut entraîner une erreur ANR.

L'exemple suivant montre que le thread principal est bloqué lors de la tentative d'obtention d'un verrou :

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)
[...]

Le thread bloquant envoie une requête HTTP pour télécharger une vidéo :

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 coûteux en ressources

Si vous affichez trop d'éléments dans un seul frame, le thread principal risque de ne pas répondre pendant la durée du frame, comme suit :

  • Affichage de nombreux éléments inutiles en dehors de l'écran.
  • Utilisation d'un algorithme inefficace, tel que O(n^2), lors de l'affichage de nombreux éléments d'interface utilisateur.

Bloqué par un autre composant

Si un autre composant, tel qu'un broadcast receiver, bloque le thread principal pendant plus de cinq secondes, cela peut entraîner des erreurs ANR lors de l'envoi des entrées et de graves à-coups.

Évitez d'effectuer des tâches lourdes sur le thread principal au niveau des composants de l'application. Dans la mesure du possible, exécutez des broadcast receivers sur un thread différent.