一个简单的看门狗,用于检测 Android ANR(应用程序无响应)。
目前 Android 应用程序无法捕获并报告 ANR 错误。
如果您的应用程序不在 Play 商店中(因为您仍在开发它,或者因为您以不同的方式分发它),则调查 ANR 的唯一方法是提取文件 /data/anr/traces.txt。
此外,我们发现使用 Play 商店不如选择我们自己的错误跟踪服务有效。
android bug tracker 中有一个问题条目描述了这一缺陷,请随意给它加注星号;)
它设置一个“看门狗”计时器,用于检测 UI 线程何时停止响应。当它发生时,它会引发所有线程堆栈跟踪的错误(首先是主线程)。
是的!我很高兴你问:这就是它最初开发的原因!
当这引发错误时,崩溃处理程序可以拦截它并按需要的方式处理它。
已知的工作崩溃报告者包括:
setReportMainThreadOnly()
)它没有理由不与[在此处插入您最喜欢的崩溃报告系统]一起使用。
看门狗是一个简单的线程,它在循环中执行以下操作:
在app/build.gradle
文件中添加:
implementation 'com.github.anrwatchdog:anrwatchdog:1.4.0'
在您的应用程序类的onCreate
中,添加:
new ANRWatchDog (). start ();
下载最新的jar包
将jar放到项目的libs/
目录下
ANRError
堆栈跟踪有点特殊,它具有应用程序中运行的所有线程的堆栈跟踪。因此,在报告中,每个caused by
并不是先前异常的原因,而是不同线程的堆栈跟踪。
这是一个死锁示例:
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)
从这个报告中,我们可以看到两个线程的堆栈跟踪。第一个(“主”线程)卡在MainActivity.java:46
处,而第二个线程(名为“App: Locker”)在MainActivity.java:18
处锁定在睡眠状态。
从这里开始,如果我们看这两行,我们一定会明白死锁的原因!
请注意,某些崩溃报告库(例如 Crashlytics)会报告未捕获异常时的所有线程堆栈跟踪。在这种情况下,让所有线程处于同一个异常中可能会很麻烦。在这种情况下,只需使用setReportMainThreadOnly()
即可。
设置不同的超时(默认为 5000 毫秒):
if ( BuildConfig . DEBUG == false ) {
new ANRWatchDog ( 10000 /*timeout*/ ). start ();
}
默认情况下,如果调试器已连接或应用程序正在等待调试器连接,则看门狗将忽略 ANR。这是因为它将执行暂停和断点检测为 ANR。要禁用此功能并抛出ANRError
,即使调试器已连接,您可以添加setIgnoreDebugger(true)
:
new ANRWatchDog (). setIgnoreDebugger ( true ). start ();
如果您不想在检测到 ANR 时使应用程序崩溃,则可以启用回调:
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 ();
在生产环境中交付应用程序时,这一点非常重要。当最终用户手中时,最好不要在 5 秒后崩溃,而只需将 ANR 报告给您使用的任何报告系统。也许再过几秒钟,应用程序就会“解冻”。
如果您希望在ANRError中只报告您自己的线程,而不是所有线程(包括FinalizerDaemon
线程等系统线程),您可以设置一个前缀:仅报告名称以此前缀开头的线程。
new ANRWatchDog (). setReportThreadNamePrefix ( "APP:" ). start ();
然后,当您启动线程时,不要忘记将其名称设置为以此前缀开头的内容(如果您希望报告它):
public class MyAmazingThread extends Thread {
@ Override
public void run () {
setName ( "APP: Amazing!" );
/* ... do amazing things ... */
}
}
如果您只想获得主线程堆栈跟踪而不是所有其他线程,您可以:
new ANRWatchDog (). setReportMainThreadOnly (). start ();
有时,您想知道应用程序已冻结一段时间,但又不想报告 ANR 错误。您可以定义一个在报告错误之前将被调用的拦截器。拦截器的作用是定义在给定的冻结持续时间内是否应引发或推迟 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 ;
}
})
在此示例中,ANRWatchDog 以 2000 毫秒的超时启动,但拦截器将推迟错误,直到达到至少 5000 毫秒的冻结时间。
ANRWatchDog 是一个线程,因此您可以随时中断它。
如果您正在使用 Android 的多进程功能进行编程(例如在新进程中启动活动),请记住每个进程都需要一个 ANRWatchDog 线程。
ANR-Watchdog 可以免费用于非营利和商业用途,并且永远如此。
如果您想对我的工作表示支持或赞赏,您可以随意捐款!
(当然)我们将非常感激,但这绝不是获得帮助或支持所必需的,我很乐意免费提供:)