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 :
|
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.