168 votes

Android - comment enquêter sur un ANR ?

Existe-t-il un moyen de savoir où mon application a lancé un ANR (Application Not Responding) ? J'ai regardé le fichier traces.txt dans /data et je vois une trace pour mon application. Voici ce que je vois dans la trace.

DALVIK THREADS:
"main" prio=5 tid=3 TIMED_WAIT
  | group="main" sCount=1 dsCount=0 s=0 obj=0x400143a8
  | sysTid=691 nice=0 sched=0/0 handle=-1091117924
  at java.lang.Object.wait(Native Method)
  - waiting on <0x1cd570> (a android.os.MessageQueue)
  at java.lang.Object.wait(Object.java:195)
  at android.os.MessageQueue.next(MessageQueue.java:144)
  at android.os.Looper.loop(Looper.java:110)
  at android.app.ActivityThread.main(ActivityThread.java:3742)
  at java.lang.reflect.Method.invokeNative(Native Method)
  at java.lang.reflect.Method.invoke(Method.java:515)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497)
  at dalvik.system.NativeStart.main(Native Method)

"Binder Thread #3" prio=5 tid=15 NATIVE
  | group="main" sCount=1 dsCount=0 s=0 obj=0x434e7758
  | sysTid=734 nice=0 sched=0/0 handle=1733632
  at dalvik.system.NativeStart.run(Native Method)

"Binder Thread #2" prio=5 tid=13 NATIVE
  | group="main" sCount=1 dsCount=0 s=0 obj=0x433af808
  | sysTid=696 nice=0 sched=0/0 handle=1369840
  at dalvik.system.NativeStart.run(Native Method)

"Binder Thread #1" prio=5 tid=11 NATIVE
  | group="main" sCount=1 dsCount=0 s=0 obj=0x433aca10
  | sysTid=695 nice=0 sched=0/0 handle=1367448
  at dalvik.system.NativeStart.run(Native Method)

"JDWP" daemon prio=5 tid=9 VMWAIT
  | group="system" sCount=1 dsCount=0 s=0 obj=0x433ac2a0
  | sysTid=694 nice=0 sched=0/0 handle=1367136
  at dalvik.system.NativeStart.run(Native Method)

"Signal Catcher" daemon prio=5 tid=7 RUNNABLE
  | group="system" sCount=0 dsCount=0 s=0 obj=0x433ac1e8
  | sysTid=693 nice=0 sched=0/0 handle=1366712
  at dalvik.system.NativeStart.run(Native Method)

"HeapWorker" daemon prio=5 tid=5 VMWAIT
  | group="system" sCount=1 dsCount=0 s=0 obj=0x4253ef88
  | sysTid=692 nice=0 sched=0/0 handle=1366472
  at dalvik.system.NativeStart.run(Native Method)

----- end 691 -----

Comment puis-je savoir où se situe le problème ? Les méthodes dans la trace sont toutes des méthodes SDK.

2 votes

Je dispose d'un rapport de ce type, qui se produit également chez android.os.MessageQueue.nativePollOnce(Native Method) . Puis-je l'ignorer sans risque ?

134voto

sooniln Points 9909

Un ANR se produit lorsqu'une longue opération a lieu dans le thread "principal". Il s'agit du thread de la boucle d'événements, et s'il est occupé, Android ne peut pas traiter d'autres événements de l'interface graphique dans l'application, et affiche donc une boîte de dialogue ANR.

Maintenant, dans la trace que vous avez postée, le fil principal semble bien fonctionner, il n'y a pas de problème. Il tourne au ralenti dans la MessageQueue, en attendant l'arrivée d'un autre message. Dans votre cas, l'ANR était probablement une opération plus longue, plutôt que quelque chose qui a bloqué le fil d'exécution de façon permanente, de sorte que le fil d'exécution de l'événement a récupéré après la fin de l'opération, et votre trace est passée après l'ANR.

Détecter où les ANR se produisent est facile s'il s'agit d'un blocage permanent (blocage de l'acquisition de certains verrous par exemple), mais plus difficile s'il s'agit juste d'un retard temporaire. Tout d'abord, passez en revue votre code et recherchez les points vulnérables et les opérations de longue durée. Il peut s'agir par exemple de l'utilisation de sockets, de verrous, de threads dormants et d'autres opérations bloquantes au sein du thread d'événement. Vous devez vous assurer que toutes ces opérations se déroulent dans des threads séparés. Si rien ne semble être le problème, utilisez le DDMS et activez la vue des threads. Cela montre tous les threads de votre application, comme la trace que vous avez. Reproduisez l'ANR, et rafraîchissez le thread principal en même temps. Cela devrait vous montrer précisément ce qui se passe au moment de l'ANR.

7 votes

Le seul problème est de "reproduire l'ANR" :-) Pourriez-vous m'expliquer comment cette trace de pile montre que le thread principal tourne au ralenti, ce serait génial.

24 votes

La trace de la pile montre que le thread principal est dans le Looper (l'implémentation de la boucle de messages) et qu'il effectue une attente temporisée via Object.wait. Cela signifie que la boucle de messages n'a actuellement aucun message à distribuer et qu'elle attend l'arrivée de nouveaux messages. Un ANR se produit lorsque le système se rend compte qu'une boucle de messages passe trop de temps à traiter un message, et ne traite pas les autres messages de la file d'attente. Si la boucle attend des messages, ce n'est évidemment pas le cas.

3 votes

@Soonil Bonjour, savez-vous ce que signifie le reste des sections comme Binder thread 3, Binder thread 2 JDWP demon prio 5. Que signifie sCount,dsCount, obj, sysTid, nice sched. Il y a aussi des informations comme VMWAIT, RUNNABLE, NATIVE.

107voto

Dheeraj V.S. Points 6384

Vous pouvez activer StrictMode dans les API de niveau 9 et plus.

StrictMode est le plus souvent utilisé pour détecter les accès accidentels au disque ou au réseau réseau sur le thread principal de l'application, où sont reçues les opérations de l'interface et les animations ont lieu. En gardant le thread principal de votre application de votre application, vous empêcher les dialogues ANR d'être montrés aux utilisateurs.

public void onCreate() {
    StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                           .detectAll()
                           .penaltyLog()
                           .penaltyDeath()
                           .build());
    super.onCreate();
}

en utilisant penaltyLog() vous pouvez regarder la sortie de adb logcat pendant que vous pendant que vous utilisez votre application pour voir les violations au fur et à mesure qu'elles se produisent.

0 votes

StrictMode ne peut être résolu en un type. Dois-je importer quelque chose d'abord ? Appuyer sur CTRL+SHIFT+O n'aide pas.

28 votes

Petite astuce - utiliser if (BuildConfig.DEBUG)... pour empêcher l'inclusion dans la production

0 votes

@uval que voulez-vous dire par "empêcher l'inclusion dans la production" ? !!

84voto

Horyun Lee Points 81

Vous vous demandez quelle tâche contient un Thread UI. Le fichier de trace vous donne un indice pour trouver la tâche. Vous devez enquêter sur l'état de chaque thread.

État du fil

  • running - exécution du code de l'application
  • dormir - appelé Thread.sleep()
  • monitor - en attente d'acquisition d'un verrou de moniteur
  • wait - dans Object.wait()
  • native - exécution de code natif
  • vmwait - attente sur une ressource VM
  • zombie - le fil est en train de mourir
  • init - le thread est en train de s'initialiser (vous ne devriez pas voir ceci)
  • starting - le fil est sur le point de commencer (vous ne devriez pas voir cela non plus)

Concentrez-vous sur l'état SUSPENDED, MONITOR. L'état Monitor indique quel thread est examiné et l'état SUSPENDED du thread est probablement la raison principale du blocage.

Étapes de l'enquête de base

  1. Trouver "en attente de verrouillage"
    • vous pouvez trouver l'état du moniteur "Fil de reliure #15" prio=5 tid=75 MONITEUR
    • vous avez de la chance si vous trouvez "en attente de verrouillage".
    • exemple : en attente du verrou <0xblahblah> (un com.foo.A) détenu par threadid=74
  2. Vous pouvez remarquer que "tid=74" contient une tâche maintenant. Donc allez à tid=74
  3. tid=74 peut-être l'état SUSPENDED ! trouver la raison principale !

La trace ne contient pas toujours "waiting to lock". Dans ce cas, il est difficile de trouver la raison principale.

1 votes

Bonne explication. Maintenant, il est plus facile pour moi de comprendre les logs ANR. Mais j'ai encore un problème pour comprendre la cause parce que dans l'étape 1, je suis capable de trouver facilement l'id du fil mais quand, dans l'étape 2, j'essaie d'aller où il est, pour vérifier l'état, je ne peux pas le trouver. Une idée sur la façon de procéder ?

2 votes

J'ai - waiting to lock an unknown object à l'intérieur de "HeapTaskDaemon" daemon prio=5 tid=8 Blocked . Qu'est-ce que cela signifie ? Quelqu'un peut-il m'aider ?

15voto

J'apprends Android depuis quelques mois, je suis donc loin d'être un expert, mais j'ai été vraiment déçu par la documentation sur les ANR.

La plupart des conseils semblent être axés sur la manière de les éviter ou de les corriger en examinant aveuglément votre code, ce qui est très bien, mais je n'ai rien trouvé sur l'analyse de la trace.

Il y a trois choses que vous devez vraiment rechercher avec les bûches ANR.

1) Blocages : Lorsqu'un thread est dans l'état WAIT, vous pouvez consulter les détails pour savoir qui est "heldby=". La plupart du temps, il sera tenu par lui-même, mais s'il est tenu par un autre thread, c'est probablement un signe de danger. Allez voir ce fil et regardez par quoi il est tenu. Vous trouverez peut-être une boucle, ce qui est un signe évident que quelque chose a mal tourné. C'est assez rare, mais c'est le premier point car quand cela arrive, c'est un cauchemar

2) Le fil principal en attente : Si votre thread principal est dans l'état WAIT, vérifiez s'il est retenu par un autre thread. Cela ne devrait pas se produire, car votre thread UI ne devrait pas être retenu par un thread d'arrière-plan.

Ces deux scénarios signifient que vous devez retravailler votre code de manière significative.

3) Opérations lourdes sur le thread principal : C'est la cause la plus fréquente des ANR, mais parfois l'une des plus difficiles à trouver et à réparer. Regardez les détails du thread principal. Faites défiler la trace de la pile jusqu'à ce que vous voyiez des classes que vous reconnaissez (de votre application). Regardez les méthodes dans la trace et déterminez si vous faites des appels au réseau, à la base de données, etc. à ces endroits.

Enfin, et je m'excuse de mettre en avant sans vergogne mon propre code, vous pouvez utiliser l'analyseur de journaux python que j'ai écrit à l'adresse suivante https://github.com/HarshEvilGeek/Android-Log-Analyzer Il va parcourir vos fichiers de logs, ouvrir les fichiers ANR, trouver les deadlocks, trouver les threads principaux en attente, trouver les exceptions non attrapées dans les logs de vos agents et imprimer le tout à l'écran d'une manière relativement facile à lire. Lisez le fichier ReadMe (que je suis sur le point d'ajouter) pour apprendre à l'utiliser. Cela m'a beaucoup aidé la semaine dernière !

4voto

Ulrich Points 31

Lorsque vous analysez des problèmes de synchronisation, le débogage n'est souvent pas utile, car le fait de geler l'application à un point d'arrêt fera disparaître le problème.

Votre meilleure chance est d'insérer de nombreux appels de journalisation (Log.XXX()) dans les différents threads et callbacks de l'application et de voir où se situe le retard. Si vous avez besoin d'un suivi de pile, créez une nouvelle exception (instanciez-en une) et enregistrez-la.

2 votes

Merci pour le conseil de créer une nouvelle exception si vous avez besoin d'un suivi de pile. C'est très utile pour le débogage :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X