Android - comment enquêter sur un ANR?

153

Existe-t-il un moyen de savoir où mon application a lancé un ANR (Application Not Responding). J'ai jeté un œil au fichier traces.txt dans / data et je vois une trace pour mon application. C'est 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 de la trace sont toutes des méthodes SDK.

Merci.

perdu
la source
2
J'ai un rapport de ce genre, qui se passe également à android.os.MessageQueue.nativePollOnce(Native Method). Puis-je l'ignorer en toute sécurité?
rds

Réponses:

124

Un ANR se produit lorsqu'une opération longue 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 GUI dans l'application, et lance ainsi 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 est inactif dans MessageQueue, en attente d'un autre message. Dans votre cas, l'ANR était probablement une opération plus longue, plutôt que quelque chose qui bloquait le thread de manière permanente, donc le thread d'événement a récupéré une fois l'opération terminée 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 ne s'agit que 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. Les exemples peuvent inclure l'utilisation de sockets, de verrous, de sommeil de thread et d'autres opérations de blocage à partir du thread d'événement. Vous devez vous assurer que tout cela se produit dans des threads séparés. Si rien ne semble être le problème, utilisez DDMS et activez la vue des threads. Cela montre tous les threads de votre application similaires à la trace que vous avez. Reproduisez l'ANR et actualisez le thread principal en même temps. Cela devrait vous montrer précisément ce qui se passe au moment de l'ANR

bientôt
la source
6
le seul problème est "reproduire l'ANR" :-). pourriez-vous s'il vous plaît expliquer comment ce spectacle de trace de pile est le thread principal est «inactif», ce serait génial.
Blundell
20
La trace de la pile montre que le thread principal se trouve dans le Looper (l'implémentation de la boucle de message) et effectue une attente chronométrée via Object.wait. Cela signifie que les boucles de messages n'ont actuellement aucun message à envoyer et qu'elles attendent 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 d'autres messages dans le queue. Si les boucles attendent des messages, cela ne se produit évidemment pas.
Sooniln
3
@Soonil Salut, savez-vous ce que le reste des sections signifie comme Binder thread 3, Binder thread 2 JDWP demon prio 5. qu'est-ce que sCount, dsCount, obj, sysTid, nice sched signifie. aussi il a des informations comme VMWAIT, RUNNABLE, NATIVE
minhaz
1
Mon application est basée sur NDK, je vois le même ANR. En outre, le fil principal est très bien. J'ai essayé DDMS et actualisé mon thread de travail lorsqu'il se bloque. Malheureusement, tout ce que j'obtiens est une seule ligne NativeStart :: run. La vue de thread DDMS est-elle même capable d'inspecter les threads NDK natifs? Aussi: StrictMode n'a rien trouvé.
Bram
6
Voir elliotth.blogspot.com/2012/08/… pour une bonne explication de la sortie.
Sooniln
96

Vous pouvez activer StrictMode dans l'API niveau 9 et supérieur.

StrictMode est le plus couramment utilisé pour intercepter l'accès accidentel au disque ou au réseau sur le thread principal de l'application, où les opérations de l'interface utilisateur sont reçues et les animations ont lieu. En gardant le thread principal de votre application réactif, vous empêchez également les boîtes de dialogue ANR d'être affichées 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 utilisez votre application pour voir les violations au fur et à mesure qu'elles se produisent.

Dheeraj Vepakomma
la source
StrictMode ne peut pas être résolu en type. Dois-je d'abord importer quelque chose? Appuyer sur CTRL + SHIFT + O n'aide pas.
kuchi
23
petite astuce - utilisez if (BuildConfig.DEBUG) ... pour empêcher l'inclusion en production
Amir Uval
@uval qu'entendez-vous par "empêcher l'inclusion dans la production"? !!
Muhammed Refaat
2
@MuhammedRefaat cela n'empêche aucun ANR. L'application plantera immédiatement au lieu de 5 secondes après. Par exemple, si vous accédez à la base de données sur le thread principal et que cela prend 2 secondes, vous n'obtiendrez pas d'ANR, mais StrictMode plantera l'application. StrictMode est strictement pour votre phase de débogage, pas pour la production.
Amir Uval
1
@MuhammedRefaat a ajouté ma réponse à votre question.
Amir Uval
80

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

État du fil

  • exécution - exécution du code d'application
  • dormir - appelé Thread.sleep ()
  • moniteur - en attente d'acquérir un verrouillage du moniteur
  • wait - dans Object.wait ()
  • native - exécution de code natif
  • vmwait - en attente d'une ressource VM
  • zombie - le fil est en train de mourir
  • init - le thread s'initialise (vous ne devriez pas voir ça)
  • démarrage - le fil est sur le point de démarrer (vous ne devriez pas voir cela non plus)

Focus sur l'état SUSPENDU, MONITOR. L'état du moniteur indique quel thread est étudié et l'état SUSPENDED du thread est probablement la principale raison du blocage.

Étapes d'enquête de base

  1. Trouvez «en attente de verrouillage»
    • vous pouvez trouver l'état du moniteur "Binder Thread # 15" prio = 5 tid = 75 MONITOR
    • vous avez de la chance si vous trouvez "en attente de verrouillage"
    • exemple: en attente de verrouiller <0xblahblah> (un com.foo.A) tenu par threadid = 74
  2. Vous pouvez remarquer que "tid = 74" tient une tâche maintenant. Alors allez à tid = 74
  3. tid = 74 peut-être l'état SUSPENDU! trouver la raison principale!

trace ne contient pas toujours "en attente de verrouillage". dans ce cas, il est difficile de trouver la raison principale.

Horyun Lee
la source
1
Belle explication. Maintenant, il est plus facile pour moi de comprendre les journaux ANR. Mais j'ai encore un problème à comprendre à cause car à l'étape 1 je suis capable de trouver facilement l'identifiant du thread mais quand, à l'étape 2, j'essaye d'aller où il se trouve, pour vérifier l'état, je ne le trouve pas . Une idée comment procéder?
THZ
1
J'ai à l' - waiting to lock an unknown objectintérieur "HeapTaskDaemon" daemon prio=5 tid=8 Blocked . Qu'est-ce que cela signifie que quelqu'un peut aider?
Hilal
13

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

La plupart des conseils semblent viser à les éviter ou à les corriger en regardant aveuglément votre code, ce qui est génial, mais je n'ai rien trouvé en analysant la trace.

Il y a trois choses que vous devez vraiment rechercher avec les journaux ANR.

1) Deadlocks: Lorsqu'un thread est dans l'état WAIT, vous pouvez regarder à travers les détails pour trouver qui il est "holdby =". La plupart du temps, il sera tenu seul, mais s'il est tenu par un autre fil, c'est probablement un signe de danger. Allez regarder ce fil et voyez ce qu'il contient. Vous pourriez trouver une boucle, ce qui indique clairement que quelque chose ne va pas. C'est assez rare, mais c'est le premier point car quand ça arrive, c'est un cauchemar

2) Fil principal en attente: Si votre fil principal est dans l'état WAIT, vérifiez s'il est détenu par un autre fil. Cela ne devrait pas se produire, car votre thread d'interface utilisateur ne doit pas être détenu 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 courante des ANR, mais parfois l'une des plus difficiles à trouver et à corriger. Regardez les principaux détails du fil. Faites défiler la trace de la pile et jusqu'à ce que vous voyiez les classes que vous reconnaissez (à partir de votre application). Regardez les méthodes de la trace et déterminez si vous effectuez des appels réseau, des appels db, etc. à ces endroits.

Enfin, et je m'excuse d'avoir branché sans vergogne mon propre code, vous pouvez utiliser l'analyseur de journaux python que j'ai écrit à https://github.com/HarshEvilGeek/Android-Log-Analyzer Cela passera par vos fichiers journaux, ouvrez les fichiers ANR, trouvez les blocages, trouvez les threads principaux en attente, trouvez les exceptions non interceptées dans vos journaux d'agent et imprimez le tout à l'écran d'une manière relativement facile à lire. Lisez le fichier ReadMe (que je suis sur le point d'ajouter) pour savoir comment l'utiliser. Cela m'a beaucoup aidé la semaine dernière!

Akhil Cherian Verghese
la source
4

Chaque fois que vous analysez des problèmes de synchronisation, le débogage n'aide souvent pas, car le gel de l'application à un point d'arrêt éliminera le problème.

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

Ulrich
la source
2
Merci pour les conseils sur la création d'une nouvelle exception si vous avez besoin d'un stacktrace. C'est très utile lors du débogage :)
kuchi
3

Qu'est-ce qui déclenche l'ANR?

En général, le système affiche un ANR si une application ne peut pas répondre à l'entrée utilisateur.

Dans toute situation dans laquelle votre application effectue une opération potentiellement longue, vous ne devez pas effectuer le travail sur le thread d'interface utilisateur, mais plutôt créer un thread de travail et y effectuer la majeure partie du travail. Cela maintient le thread d'interface utilisateur (qui pilote la boucle d'événements de l'interface utilisateur) en cours d'exécution et empêche le système de conclure que votre code est gelé.

Comment éviter les ANR

Les applications Android fonctionnent normalement entièrement sur un seul thread (par défaut le "thread UI" ou "thread principal"). Cela signifie que tout ce que votre application fait dans le thread d'interface utilisateur qui prend beaucoup de temps peut déclencher la boîte de dialogue ANR car votre application ne se donne pas la possibilité de gérer l'événement d'entrée ou les diffusions d'intention.

Par conséquent, toute méthode qui s'exécute dans le thread d'interface utilisateur doit effectuer le moins de travail possible sur ce thread. En particulier, les activités doivent faire le moins possible pour mettre en place des méthodes clés du cycle de vie telles que onCreate () et onResume (). Les opérations potentiellement longues, telles que les opérations de réseau ou de base de données, ou les calculs coûteux en calcul comme le redimensionnement des bitmaps doivent être effectués dans un thread de travail (ou dans le cas d'opérations de bases de données, via une requête asynchrone).

Code: thread de travail avec la classe AsyncTask

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    // Do the long-running work in here
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++) {
            totalSize += Downloader.downloadFile(urls[i]);
            publishProgress((int) ((i / (float) count) * 100));
            // Escape early if cancel() is called
            if (isCancelled()) break;
        }
        return totalSize;
    }

    // This is called each time you call publishProgress()
    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }

    // This is called when doInBackground() is finished
    protected void onPostExecute(Long result) {
        showNotification("Downloaded " + result + " bytes");
    }
}

Code: Exécuter le thread de travail

Pour exécuter ce thread de travail, créez simplement une instance et appelez execute ():

new DownloadFilesTask().execute(url1, url2, url3);

La source

http://developer.android.com/training/articles/perf-anr.html

Jack
la source
1

mon problème avec ANR, après beaucoup de travail, j'ai découvert qu'un fil appelait une ressource qui n'existait pas dans la mise en page, au lieu de renvoyer une exception, j'ai eu ANR ...

yaniv
la source
c'est extrêmement bizarre
Nilabja
0

Basique sur la réponse @Horyun Lee, j'ai écrit un petit script python pour aider à enquêter sur ANR à partir de traces.txt.

Les ANR seront affichés sous forme de graphiques graphvizsi vous les avez installés grapvhvizsur votre système.

$ ./anr.py --format png ./traces.txt

Un png sera affiché comme ci-dessous s'il y a des ANR détectés dans le fichier traces.txt. C'est plus intuitif.

entrez la description de l'image ici

Le traces.txtfichier d' exemple utilisé ci-dessus a été obtenu à partir d' ici .

alijandro
la source
0

Envisagez d'utiliser la bibliothèque ANR-Watchdog pour suivre et capturer avec précision les traces de pile ANR avec un niveau de détail élevé. Vous pouvez ensuite les envoyer à votre bibliothèque de rapports de plantage. Je recommande d'utilisersetReportMainThreadOnly() dans ce scénario. Vous pouvez soit faire en sorte que l'application lève une exception non fatale du point de gel, soit forcer l'application à se fermer lorsque l'ANR se produit.

Notez que les rapports ANR standard envoyés à votre console développeur Google Play ne sont souvent pas suffisamment précis pour identifier le problème exact. C'est pourquoi une bibliothèque tierce est nécessaire.

Mr-IDE
la source