Android - wie untersuche ich eine ANR?

152

Gibt es eine Möglichkeit herauszufinden, wo meine App eine ANR (Application Not Responding) ausgelöst hat? Ich habe mir die Datei traces.txt in / data angesehen und sehe eine Ablaufverfolgung für meine Anwendung. Das sehe ich in der Spur.

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

Wie kann ich herausfinden, wo das Problem liegt? Die Methoden im Trace sind alle SDK-Methoden.

Vielen Dank.

lostInTransit
quelle
2
Ich habe einen Bericht dieser Art, der auch bei stattfindet android.os.MessageQueue.nativePollOnce(Native Method). Kann ich es ignorieren?
rds

Antworten:

124

Eine ANR tritt auf, wenn eine lange Operation im "Haupt" -Thread stattfindet. Dies ist der Ereignisschleifenthread. Wenn er ausgelastet ist, kann Android keine weiteren GUI-Ereignisse in der Anwendung verarbeiten und löst daher einen ANR-Dialog aus.

In der von Ihnen geposteten Ablaufverfolgung scheint der Hauptthread in Ordnung zu sein, es gibt kein Problem. Es befindet sich in der MessageQueue im Leerlauf und wartet darauf, dass eine weitere Nachricht eingeht. In Ihrem Fall war die ANR wahrscheinlich eine längere Operation und nicht etwas, das den Thread dauerhaft blockiert hat, sodass der Ereignisthread nach Abschluss der Operation wiederhergestellt wurde und Ihre Ablaufverfolgung durchlaufen wurde nach dem ANR.

Das Erkennen, wo ANRs auftreten, ist einfach, wenn es sich um einen permanenten Block handelt (z. B. Deadlock beim Erwerb einiger Sperren), aber schwieriger, wenn es sich nur um eine vorübergehende Verzögerung handelt. Gehen Sie zunächst Ihren Code durch und suchen Sie nach gefährdeten Stellen und langfristigen Vorgängen. Beispiele können die Verwendung von Sockets, Sperren, Thread-Ruhezuständen und anderen Blockierungsvorgängen innerhalb des Ereignisthreads umfassen. Sie sollten sicherstellen, dass dies alles in separaten Threads geschieht. Wenn nichts das Problem zu sein scheint, verwenden Sie DDMS und aktivieren Sie die Thread-Ansicht. Hier werden alle Threads in Ihrer Anwendung angezeigt, die der von Ihnen verwendeten Ablaufverfolgung ähneln. Reproduzieren Sie die ANR und aktualisieren Sie gleichzeitig den Haupt-Thread. Das sollte Ihnen genau zeigen, was zum Zeitpunkt der ANR los ist

bald
quelle
6
Das einzige Problem ist "ANR reproduzieren" :-). Könntest du bitte erklären, wie der Hauptthread dieser Stack-Trace-Show im Leerlauf läuft? Das wäre großartig.
Blundell
20
Die Stapelverfolgung zeigt, dass sich der Hauptthread im Looper befindet (die Implementierung der Nachrichtenschleife) und eine zeitgesteuerte Wartezeit über Object.wait ausführt. Dies bedeutet, dass die Nachrichtenschleifen derzeit keine zu versendenden Nachrichten haben und auf den Eingang neuer Nachrichten warten. Eine ANR tritt auf, wenn das System feststellt, dass eine Nachrichtenschleife zu viel Zeit für die Verarbeitung einer Nachricht und nicht für die Verarbeitung anderer Nachrichten in der Nachrichtenschleife benötigt Warteschlange. Wenn die Schleifen auf Nachrichten warten, geschieht dies offensichtlich nicht.
bald
3
@Soonil Hallo, wissen Sie, was der Rest der Abschnitte wie Binder-Thread 3, Binder-Thread 2 bedeutet? JDWP Demon Prio 5. Was ist sCount, dsCount, obj, sysTid? Es hat auch Informationen wie VMWAIT, RUNNABLE, NATIVE
Minhaz
1
Meine App basiert auf NDK, ich sehe die gleiche ANR. Auch der Hauptfaden ist in Ordnung. Ich habe DDMS ausprobiert und meinen Worker-Thread aktualisiert, wenn er einfriert. Leider bekomme ich nur eine einzeilige NativeStart :: run. Ist die DDMS-Thread-Ansicht überhaupt in der Lage, native NDK-Threads zu überprüfen? Auch: StrictMode hat nichts gefunden.
Bram
6
Eine gute Erklärung der Ausgabe finden Sie unter elliotth.blogspot.com/2012/08/… .
bald
96

Sie können StrictMode in API-Level 9 und höher aktivieren .

StrictMode wird am häufigsten verwendet, um versehentlichen Festplatten- oder Netzwerkzugriff auf den Hauptthread der Anwendung abzufangen, in dem UI-Vorgänge empfangen und Animationen stattfinden. Indem Sie den Hauptthread Ihrer Anwendung reaktionsfähig halten, verhindern Sie auch, dass den Benutzern ANR-Dialoge angezeigt werden.

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

Mit können penaltyLog()Sie die Ausgabe von adb logcat beobachten, während Sie Ihre Anwendung verwenden, um die auftretenden Verstöße zu sehen.

Dheeraj Vepakomma
quelle
StrictMode kann nicht in einen Typ aufgelöst werden. Muss ich zuerst etwas importieren? Das Drücken von STRG + UMSCHALT + O hilft nicht.
Kuchi
23
kleiner Tipp - verwenden Sie if (BuildConfig.DEBUG) ... um die Aufnahme in die Produktion zu verhindern
Amir Uval
@uval was meinst du mit "um die Aufnahme in die Produktion zu verhindern"? !!
Muhammed Refaat
2
@ MuhammedRefaat verhindert keine ANR. Die App stürzt sofort statt nach 5 Sekunden ab. Wenn Sie beispielsweise auf die Datenbank im Hauptthread zugreifen und dies 2 Sekunden dauert, erhalten Sie keine ANR, aber StrictMode stürzt die App ab. StrictMode ist ausschließlich für Ihre Debugging-Phase vorgesehen, nicht für die Produktion.
Amir Uval
1
@MuhammedRefaat hat meine Antwort auf Ihre Frage hinzugefügt.
Amir Uval
80

Sie fragen sich, welche Aufgabe einen UI-Thread enthält. Die Trace-Datei gibt Ihnen einen Hinweis, um die Aufgabe zu finden. Sie müssen den Status jedes Threads untersuchen

Zustand des Fadens

  • Ausführen - Ausführen von Anwendungscode
  • Schlafen - genannt Thread.sleep ()
  • Monitor - Warten auf eine Monitorsperre
  • warten - in Object.wait ()
  • native - Ausführen von nativem Code
  • vmwait - Warten auf eine VM-Ressource
  • Zombie-Thread ist im Sterben begriffen
  • init - thread wird initialisiert (das sollte man nicht sehen)
  • Start - Thread ist im Begriff zu starten (das solltest du auch nicht sehen)

Konzentrieren Sie sich auf den Status SUSPENDED, MONITOR. Der Überwachungsstatus gibt an, welcher Thread untersucht wird, und der SUSPENDED-Status des Threads ist wahrscheinlich der Hauptgrund für einen Deadlock.

Grundlegende Untersuchungsschritte

  1. Finden Sie "Warten auf Sperre"
    • Sie finden den Monitorstatus "Binder Thread # 15" prio = 5 tid = 75 MONITOR
    • Sie haben Glück, wenn Sie "Warten auf Sperre" finden
    • Beispiel: Warten auf das Sperren von <0xblahblah> (a com.foo.A), das von threadid = 74 gehalten wird
  2. Sie können feststellen, dass "tid = 74" jetzt eine Aufgabe enthält. Gehen Sie also zu tid = 74
  3. tid = 74 vielleicht SUSPENDED state! Hauptgrund finden!

Trace enthält nicht immer "Warten auf Sperre". In diesem Fall ist es schwierig, den Hauptgrund zu finden.

Horyun Lee
quelle
1
Schöne Erklärung. Jetzt ist es für mich einfacher, ANR-Protokolle zu verstehen. Aber ich habe immer noch ein Problem zu verstehen, weil ich in Schritt 1 die Thread-ID leicht finden kann, aber wenn ich in Schritt 2 versuche, dorthin zu gehen, wo sie ist, um den Status zu überprüfen, kann ich sie nicht finden . Irgendeine Idee, wie man damit fortfährt?
THZ
1
Ich habe - waiting to lock an unknown objectdrinnen "HeapTaskDaemon" daemon prio=5 tid=8 Blocked . Was bedeutet es, kann jemand helfen?
Hilal
13

Ich habe in den letzten Monaten Android gelernt, bin also weit entfernt von einem Experten, aber ich war wirklich enttäuscht von der Dokumentation zu ANRs.

Die meisten Ratschläge scheinen darauf ausgerichtet zu sein, sie zu vermeiden oder zu beheben, indem Sie blind durch Ihren Code schauen. Das ist großartig, aber ich konnte bei der Analyse der Spur nichts finden.

Es gibt drei Dinge, auf die Sie bei ANR-Protokollen wirklich achten müssen.

1) Deadlocks: Wenn sich ein Thread im Status WAIT befindet, können Sie die Details durchsehen, um herauszufinden, wer "holdby =" ist. Meistens wird es von selbst gehalten, aber wenn es von einem anderen Thread gehalten wird, ist dies wahrscheinlich ein Gefahrenzeichen. Schauen Sie sich diesen Thread an und sehen Sie, was er enthält. Möglicherweise finden Sie eine Schleife, was ein klares Zeichen dafür ist, dass etwas schief gelaufen ist. Dies ist ziemlich selten, aber es ist der erste Punkt, denn wenn es passiert, ist es ein Albtraum

2) Warten auf Hauptthread: Wenn sich Ihr Hauptthread im Status WAIT befindet, überprüfen Sie, ob er von einem anderen Thread gehalten wird. Dies sollte nicht passieren, da Ihr UI-Thread nicht von einem Hintergrund-Thread gehalten werden sollte.

In beiden Fällen müssen Sie Ihren Code erheblich überarbeiten.

3) Schwere Operationen am Haupt-Thread: Dies ist die häufigste Ursache für ANRs, aber manchmal eine der schwieriger zu findenden und zu behebenden. Schauen Sie sich die Details des Haupt-Threads an. Scrollen Sie im Stack-Trace nach unten und bis Sie Klassen sehen, die Sie erkennen (aus Ihrer App). Sehen Sie sich die Methoden in der Ablaufverfolgung an und finden Sie heraus, ob Sie an diesen Stellen Netzwerkanrufe, Datenbankanrufe usw. tätigen.

Schließlich, und ich entschuldige mich dafür, dass ich meinen eigenen Code schamlos eingesteckt habe, können Sie den Python-Protokollanalysator verwenden, den ich unter https://github.com/HarshEvilGeek/Android-Log-Analyzer geschrieben habe. Dieser wird Ihre Protokolldateien durchsuchen, ANR-Dateien öffnen und finden Deadlocks, wartende Haupt-Threads finden, nicht erfasste Ausnahmen in Ihren Agentenprotokollen finden und alles auf relativ einfach zu lesende Weise auf dem Bildschirm ausdrucken. Lesen Sie die ReadMe-Datei (die ich hinzufügen möchte), um zu erfahren, wie Sie sie verwenden. Es hat mir in der letzten Woche eine Menge geholfen!

Akhil Cherian Verghese
quelle
4

Wenn Sie Zeitprobleme analysieren, hilft das Debuggen oft nicht, da das Problem durch Einfrieren der App an einem Haltepunkt behoben wird.

Am besten fügen Sie viele Protokollierungsanrufe (Log.XXX ()) in die verschiedenen Threads und Rückrufe der App ein und sehen, wo die Verzögerung liegt. Wenn Sie eine Stapelverfolgung benötigen, erstellen Sie eine neue Ausnahme (instanziieren Sie einfach eine) und protokollieren Sie sie.

Ulrich
quelle
2
Vielen Dank für den Rat zum Erstellen einer neuen Ausnahme, wenn Sie einen Stacktrace benötigen. Das ist sehr hilfreich beim Debuggen :)
Kuchi
3

Was löst ANR aus?

Im Allgemeinen zeigt das System eine ANR an, wenn eine Anwendung nicht auf Benutzereingaben reagieren kann.

In jeder Situation, in der Ihre App einen möglicherweise langwierigen Vorgang ausführt, sollten Sie die Arbeit nicht am UI-Thread ausführen, sondern stattdessen einen Arbeitsthread erstellen und den größten Teil der Arbeit dort ausführen. Dies hält den UI-Thread (der die Ereignisschleife der Benutzeroberfläche steuert) am Laufen und verhindert, dass das System zu dem Schluss kommt, dass Ihr Code eingefroren ist.

So vermeiden Sie ANRs

Android-Anwendungen werden normalerweise vollständig auf einem einzelnen Thread ausgeführt (standardmäßig "UI-Thread" oder "Haupt-Thread"). Dies bedeutet, dass alles, was Ihre Anwendung im UI-Thread tut, was lange dauert, den ANR-Dialog auslösen kann, da Ihre Anwendung sich selbst keine Chance gibt, das Eingabeereignis oder Absichtsübertragungen zu verarbeiten.

Daher sollte jede Methode, die im UI-Thread ausgeführt wird, so wenig Arbeit wie möglich an diesem Thread leisten. Insbesondere sollten Aktivitäten so wenig wie möglich dazu beitragen, wichtige Lebenszyklusmethoden wie onCreate () und onResume () einzurichten. Potenziell lange laufende Vorgänge wie Netzwerk- oder Datenbankvorgänge oder rechenintensive Berechnungen wie das Ändern der Größe von Bitmaps sollten in einem Arbeitsthread (oder im Fall von Datenbankvorgängen über eine asynchrone Anforderung) durchgeführt werden.

Code: Worker-Thread mit der AsyncTask-Klasse

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: Worker-Thread ausführen

Um diesen Arbeitsthread auszuführen, erstellen Sie einfach eine Instanz und rufen Sie execute () auf:

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

Quelle

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

Jack
quelle
1

Mein Problem mit ANR, nach viel Arbeit fand ich heraus, dass ein Thread eine Ressource aufrief, die nicht im Layout vorhanden war, anstatt eine Ausnahme zurückzugeben, bekam ich ANR ...

Yaniv
quelle
das ist extrem komisch
Nilabja
0

Grundlegend für die Antwort von @Horyun Lee habe ich ein kleines Python- Skript geschrieben , um die ANR von zu untersuchentraces.txt .

Die ANRs werden als Grafiken ausgegeben, graphvizwenn Sie sie grapvhvizauf Ihrem System installiert haben .

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

Ein PNG wird wie unten ausgegeben, wenn in der Datei ANRs erkannt werden traces.txt. Es ist intuitiver.

Geben Sie hier die Bildbeschreibung ein

Die traces.txtoben verwendete Beispieldatei wurde von hier abgerufen .

Alijandro
quelle
0

Erwägen Sie die Verwendung der ANR-Watchdog- Bibliothek, um ANR-Stapelspuren mit hoher Detailgenauigkeit genau zu verfolgen und zu erfassen. Sie können sie dann an Ihre Crash-Reporting-Bibliothek senden. Ich empfehle die VerwendungsetReportMainThreadOnly() in diesem Szenario zu verwenden. Sie können entweder festlegen, dass die App eine nicht schwerwiegende Ausnahme vom Einfrierpunkt auslöst, oder die App erzwingen, wenn die ANR auftritt.

Beachten Sie, dass die an Ihre Google Play Developer-Konsole gesendeten Standard-ANR-Berichte häufig nicht genau genug sind, um das genaue Problem zu ermitteln. Aus diesem Grund wird eine Bibliothek eines Drittanbieters benötigt.

Mr-IDE
quelle