前言
这个问题是面试的时候被问到的,问题原话:”怎么查看 ANR Log?”。
我们写程序其实并不仅仅是避免 ANR,而是要让应用体验”如丝般顺滑”。
引发 ANR 的原因
当用户的输入事件(Input Event,如按键或触摸)在5秒内得不到响应 或者 BroadcastReceiver 在 10秒内未做完,就会引发 ANR。一般来说,引起 ANR 的原因:
- 主线程有耗时的计算操作。
- 主线程在等待获取某个资源(可能是死锁)。
- 主线程睡眠太久。
简单地说就是主线程卡住了,导致用户的输入事件处理不了。
分析 ANR 的原因
当发生 ANR 时,我们可以从两部分查看原因:
- 初步:Logcat 的 ANR 日志查看 CPU 使用率。如果 CPU 使用率很高,表明有耗时计算。
- 深入:查看 “/data/anr/traces.txt” 文件。
Logcat ANR Log
这块日志主要分为三部分。
第一部分:
|
从上面看出导致 ANR 的原因是 “keyDispatchingTimeOut”。
第二部分:
|
这部分显示在 ANR 发生之前 CPU 使用状况。
第三部分:
|
这部分主要输出 ANR 这段时间内 CPU 使用状况,如果 CPU 使用率特别高,可能就能表明主线程有耗时的计算操作。
traces.txt
在查看文件内容之前,我们需要了解线程的所有状态。
- THREAD_ZOMBIE: 线程已经终止了。
- THREAD_RUNNING: 线程是 Runnable 的或者正在运行的。
- THREAD_TIMED_WAIT: 线程调用
Object.wait(long millis)
时阻塞。 - THREAD_MONITOR: 线程在获取锁时阻塞。
- THREAD_WAIT: 线程调用
Object.wait()
时阻塞。 - THREAD_INITIALIZING: 线程还没准备好,不能被运行。
- THREAD_STARTING: 线程还没准备好,不能被运行。
- THREAD_NATIVE: 线程在执行 Native Method。
- THREAD_VMWAIT: 线程在等待 VM 的资源。
- THREAD_SUSPENDED: 线程已经被挂起(暂停),在等待被恢复(resume)。最典型的状况是GC线程在工作,主线程被挂起。
状态转换图如下:
在 traces.txt 文件的开头就是最近这次 ANR 的日志。
这里我们通过几个例子来看看如何分析日志。
1、在按钮的 onClick() 中调用 Thread.sleep(),日志如下:
|
2、主线程和子线程产生死锁,日志如下:
|