Un perro guardián simple que detecta ANR (aplicación que no responde) de Android.
Actualmente no hay forma de que una aplicación de Android detecte e informe errores ANR.
Si su aplicación no está en Play Store (ya sea porque todavía la está desarrollando o porque la está distribuyendo de manera diferente), la única forma de investigar un ANR es extraer el archivo /data/anr/traces.txt.
Además, descubrimos que usar Play Store no era tan efectivo como poder elegir nuestro propio servicio de seguimiento de errores.
Hay una entrada de problema en el rastreador de errores de Android que describe esta falta, no dudes en destacarla;)
Configura un temporizador de "vigilancia" que detectará cuándo el subproceso de la interfaz de usuario deja de responder. Cuando lo hace, genera un error con todos los seguimientos de la pila de subprocesos (principal primero).
¡Sí! Me alegra que hayas preguntado: ¡esa es la razón por la que se desarrolló en primer lugar!
Como esto genera un error, un controlador de fallas puede interceptarlo y manejarlo de la manera que necesite.
Los reporteros de accidentes laborales conocidos incluyen:
setReportMainThreadOnly()
)Y no hay ninguna razón por la que no deba funcionar con [inserte aquí su sistema de informe de fallos favorito] .
El guardián es un hilo simple que hace lo siguiente en un bucle:
En el archivo app/build.gradle
, agregue:
implementation 'com.github.anrwatchdog:anrwatchdog:1.4.0'
En su clase de aplicación, en onCreate
, agregue:
new ANRWatchDog (). start ();
Descargue el último frasco
Coloque el jar en el directorio libs/
de su proyecto
El seguimiento de la pila ANRError
es un poco particular, tiene los seguimientos de la pila de todos los subprocesos que se ejecutan en su aplicación. Entonces, en el informe, cada sección caused by
no es la causa de la excepción precedente , sino el seguimiento de la pila de un subproceso diferente.
Aquí hay un ejemplo de bloqueo muerto:
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)
En este informe, podemos ver que la pila rastrea dos subprocesos. El primero (el hilo "principal") está bloqueado en MainActivity.java:46
mientras que el segundo hilo (llamado "App: Locker") está bloqueado en suspensión en MainActivity.java:18
.
A partir de ahí, si miráramos esas dos líneas, ¡seguramente entenderíamos la causa del bloqueo!
Tenga en cuenta que algunas bibliotecas de informes de fallos (como Crashlytics) informan todos los seguimientos de la pila de subprocesos en el momento de una excepción no detectada. En ese caso, tener todos los subprocesos en la misma excepción puede resultar engorroso. En tales casos, simplemente use setReportMainThreadOnly()
.
Para establecer un tiempo de espera diferente (5000 milisegundos es el valor predeterminado):
if ( BuildConfig . DEBUG == false ) {
new ANRWatchDog ( 10000 /*timeout*/ ). start ();
}
De forma predeterminada, el perro guardián ignorará los ANR si el depurador está conectado o si la aplicación está esperando que se conecte el depurador. Esto se debe a que detecta pausas de ejecución y puntos de interrupción como ANR. Para deshabilitar esto y generar un ANRError
incluso si el depurador está conectado, puede agregar setIgnoreDebugger(true)
:
new ANRWatchDog (). setIgnoreDebugger ( true ). start ();
Si prefiere no bloquear la aplicación cuando se detecta un ANR, puede habilitar una devolución de llamada:
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 ();
Esto es muy importante al entregar su aplicación en producción. Cuando está en manos del usuario final, probablemente sea mejor no fallar después de 5 segundos, sino simplemente informar el ANR a cualquier sistema de informes que utilice. Quizás, después de unos segundos más, la aplicación se "descongelará".
Si desea que solo se informen sus propios subprocesos en ANRError, y no todos los subprocesos (incluidos los subprocesos del sistema como el subproceso FinalizerDaemon
), puede establecer un prefijo: solo se informarán los subprocesos cuyo nombre comience con este prefijo. .
new ANRWatchDog (). setReportThreadNamePrefix ( "APP:" ). start ();
Luego, cuando inicies un hilo, no olvides establecer su nombre con algo que comience con este prefijo (si quieres que se informe):
public class MyAmazingThread extends Thread {
@ Override
public void run () {
setName ( "APP: Amazing!" );
/* ... do amazing things ... */
}
}
Si desea tener solo el seguimiento de la pila del subproceso principal y no todos los demás subprocesos, puede:
new ANRWatchDog (). setReportMainThreadOnly (). start ();
A veces, desea saber que la aplicación se ha congelado durante un período determinado, pero no informar el error ANR todavía. Puede definir un interceptor al que se llamará antes de informar un error. La función del interceptor es definir si, dada la duración de congelación dada, se debe generar o posponer un error ANR.
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 ;
}
})
En este ejemplo, ANRWatchDog comienza con un tiempo de espera de 2000 ms, pero el interceptor pospondrá el error hasta que se alcance al menos 5000 ms de congelación.
ANRWatchDog es un hilo, por lo que puedes interrumpirlo en cualquier momento.
Si está programando con la capacidad multiproceso de Android (como iniciar una actividad en un nuevo proceso), recuerde que necesitará un subproceso ANRWatchDog por proceso.
ANR-Watchdog es de uso gratuito tanto para uso comercial como sin fines de lucro y siempre lo será.
Si deseas mostrar algún apoyo o agradecimiento a mi trabajo, ¡puedes donar !
Esto sería (por supuesto) muy apreciado, pero de ninguna manera es necesario para recibir ayuda o soporte, que estaré encantado de brindarte de forma gratuita :)