Ein einfacher Watchdog, der Android-ANRs (Application Not Responding) erkennt.
Derzeit gibt es für eine Android-Anwendung keine Möglichkeit, ANR-Fehler zu erkennen und zu melden.
Wenn sich Ihre Anwendung nicht im Play Store befindet (entweder weil Sie sie noch entwickeln oder weil Sie sie anders verteilen), besteht die einzige Möglichkeit, eine ANR zu untersuchen, darin, die Datei /data/anr/traces.txt abzurufen.
Darüber hinaus haben wir festgestellt, dass die Nutzung des Play Stores nicht so effektiv ist wie die Möglichkeit, unseren eigenen Fehlerverfolgungsdienst auszuwählen.
Es gibt einen Problemeintrag im Android-Bug-Tracker, der diesen Mangel beschreibt. Sie können ihn gerne markieren ;)
Es richtet einen „Watchdog“-Timer ein, der erkennt, wenn der UI-Thread nicht mehr reagiert. Wenn dies der Fall ist, wird ein Fehler mit allen Thread-Stack-Traces (Hauptthread zuerst) ausgelöst.
Ja! Ich freue mich, dass Sie gefragt haben: Das ist der Grund, warum es überhaupt entwickelt wurde!
Da dies einen Fehler auslöst, kann ein Crash-Handler ihn abfangen und wie erforderlich behandeln.
Zu den bekannten Unfallreportern gehören:
setReportMainThreadOnly()
)Und es gibt keinen Grund, warum es nicht mit [fügen Sie hier Ihr bevorzugtes Absturzmeldesystem ein] funktionieren sollte.
Der Watchdog ist ein einfacher Thread, der in einer Schleife Folgendes ausführt:
Fügen Sie in der Datei app/build.gradle
Folgendes hinzu:
implementation 'com.github.anrwatchdog:anrwatchdog:1.4.0'
Fügen Sie in Ihrer Anwendungsklasse in onCreate
Folgendes hinzu:
new ANRWatchDog (). start ();
Laden Sie das neueste Glas herunter
Legen Sie die JAR-Datei im libs/
-Verzeichnis Ihres Projekts ab
Der ANRError
-Stack-Trace ist etwas speziell, er enthält die Stack-Traces aller Threads, die in Ihrer Anwendung ausgeführt werden. Im Bericht ist also jeder caused by
Abschnitt nicht die Ursache der vorherigen Ausnahme , sondern der Stack-Trace eines anderen Threads.
Hier ist ein Dead-Lock-Beispiel:
FATAL EXCEPTION: |ANR-WatchDog|
Process: anrwatchdog.github.com.testapp, PID: 26737
com.github.anrwatchdog.ANRError: Application Not Responding
Caused by: com.github.anrwatchdog.ANRError$_$_Thread: main (state = WAITING)
at testapp.MainActivity$1.run(MainActivity.java:46)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
Caused by: com.github.anrwatchdog.ANRError$_$_Thread: APP: Locker (state = TIMED_WAITING)
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:1031)
at java.lang.Thread.sleep(Thread.java:985)
at testapp.MainActivity.SleepAMinute(MainActivity.java:18)
at testapp.MainActivity.access$100(MainActivity.java:12)
at testapp.MainActivity$LockerThread.run(MainActivity.java:36)
Aus diesem Bericht können wir die Stapelspuren von zwei Threads erkennen. Der erste (der „Haupt“-Thread) bleibt bei MainActivity.java:46
hängen, während der zweite Thread (mit dem Namen „App: Locker“) bei MainActivity.java:18
im Ruhezustand gesperrt ist.
Von da an würden wir, wenn wir uns diese beiden Zeilen ansehen würden, sicherlich die Ursache für den Deadlock verstehen!
Beachten Sie, dass einige Absturzberichtsbibliotheken (z. B. Crashlytics) alle Thread-Stack-Traces zum Zeitpunkt einer nicht abgefangenen Ausnahme melden. In diesem Fall kann es umständlich sein, alle Threads in derselben Ausnahme zu haben. In solchen Fällen verwenden Sie einfach setReportMainThreadOnly()
.
So legen Sie ein anderes Zeitlimit fest (5000 Millis ist die Standardeinstellung):
if ( BuildConfig . DEBUG == false ) {
new ANRWatchDog ( 10000 /*timeout*/ ). start ();
}
Standardmäßig ignoriert der Watchdog ANRs, wenn der Debugger angeschlossen ist oder die App auf die Verbindung des Debuggers wartet. Dies liegt daran, dass Ausführungspausen und Haltepunkte als ANRs erkannt werden. Um dies zu deaktivieren und einen ANRError
auszulösen, auch wenn der Debugger verbunden ist, können Sie setIgnoreDebugger(true)
hinzufügen:
new ANRWatchDog (). setIgnoreDebugger ( true ). start ();
Wenn Sie möchten, dass die Anwendung nicht abstürzt, wenn eine ANR erkannt wird, können Sie stattdessen einen Rückruf aktivieren:
new ANRWatchDog (). setANRListener ( new ANRWatchDog . ANRListener () {
@ Override
public void onAppNotResponding ( ANRError error ) {
// Handle the error. For example, log it to HockeyApp:
ExceptionHandler . saveException ( error , new CrashManager ());
}
}). start ();
Dies ist sehr wichtig, wenn Sie Ihre App in der Produktion bereitstellen. Wenn es in der Hand des Endbenutzers ist, ist es wahrscheinlich besser, nicht nach 5 Sekunden abzustürzen, sondern die ANR einfach an das von Ihnen verwendete Berichtssystem zu melden. Möglicherweise wird die App nach einigen weiteren Sekunden „auftauen“.
Wenn Sie möchten, dass im ANRError nur Ihre eigenen Threads gemeldet werden und nicht alle Threads (einschließlich Systemthreads wie der FinalizerDaemon
-Thread), können Sie ein Präfix festlegen: Nur die Threads, deren Name mit diesem Präfix beginnt, werden gemeldet .
new ANRWatchDog (). setReportThreadNamePrefix ( "APP:" ). start ();
Wenn Sie dann einen Thread starten, vergessen Sie nicht, seinen Namen auf etwas festzulegen, das mit diesem Präfix beginnt (wenn Sie möchten, dass er gemeldet wird):
public class MyAmazingThread extends Thread {
@ Override
public void run () {
setName ( "APP: Amazing!" );
/* ... do amazing things ... */
}
}
Wenn Sie nur den Haupt-Thread-Stack-Trace und nicht alle anderen Threads haben möchten, können Sie Folgendes tun:
new ANRWatchDog (). setReportMainThreadOnly (). start ();
Manchmal möchten Sie wissen, dass die Anwendung für eine bestimmte Zeit eingefroren ist, den ANR-Fehler aber noch nicht melden. Sie können einen Interceptor definieren, der aufgerufen wird, bevor ein Fehler gemeldet wird. Die Rolle des Interceptors besteht darin, zu definieren, ob angesichts der gegebenen Einfrierdauer ein ANR-Fehler ausgelöst oder verschoben werden soll oder nicht.
new ANRWatchDog ( 2000 ). setANRInterceptor ( new ANRWatchDog . ANRInterceptor () {
@ Override
public long intercept ( long duration ) {
long ret = 5000 - duration ;
if ( ret > 0 ) {
Log . w ( TAG , "Intercepted ANR that is too short (" + duration + " ms), postponing for " + ret + " ms." );
}
return ret ;
}
})
In diesem Beispiel startet der ANRWatchDog mit einem Timeout von 2000 ms, aber der Interceptor verschiebt den Fehler, bis mindestens 5000 ms Freeze erreicht sind.
ANRWatchDog ist ein Thread, Sie können ihn also jederzeit unterbrechen.
Wenn Sie mit der Multiprozessfähigkeit von Android programmieren (z. B. das Starten einer Aktivität in einem neuen Prozess), denken Sie daran, dass Sie pro Prozess einen ANRWatchDog-Thread benötigen.
Die Nutzung von ANR-Watchdog ist sowohl für gemeinnützige als auch für kommerzielle Zwecke kostenlos und wird es auch immer sein.
Wenn Sie meine Arbeit unterstützen oder wertschätzen möchten, können Sie gerne spenden !
Das würde mich (natürlich) sehr freuen, ist aber keineswegs notwendig, um Hilfe oder Unterstützung zu erhalten, die ich gerne kostenlos zur Verfügung stelle :)