ANR (Application Not Responding)应用程序无响应。如果应用程序在UI线程处理阻塞状态的时间过长,会触发ANR。
Android系统中,ActivityManagerService(简称AMS)和WindowManagerService(简称WMS)会检测App的响应时间,如果App在特定时间无法响应屏幕触摸或键盘输入事件,或者特定事件没有处理完毕,就会出现ANR。
官方文档:https://developer.android.google.cn/topic/performance/vitals/anr?hl=zh_cn
1、ANR类型
1、KeyDispatchTimeout (主要类型):
5s内未响应用户input事件(如键盘输入, 触摸屏幕等)
Logcat日志关键字:Input event dispatching timed out
2、BroadcastTimeout
前台Broadcast:onReceiver在10s内未处理完成
后台Broadcast:onReceiver在60s内未处理完成
Logcat日志关键字:Timeout of broadcast BroadcastRecord
3、ServiceTimeout
前台Service:onCreate,onStart,onBind等生命周期在20s内未处理完成
后台Service:onCreate,onStart,onBind等生命周期在200s内未处理完成
Logcat日志关键字:Timeout executing service
4、ContentProviderTimeout
10s内未处理完成
Logcat日志关键字:timeout publishing content providers
2、ANR原因
-
应用层导致ANR
- 主线程函数阻塞:如死循环、主线程IO等
- 多线程操作的死锁,主线程被block
- 内存紧张:系统分配给一个应用内存是有限的,长期处于内存紧张会导致内存交换,进而导致一些操作超时
-
系统导致ANR
CPU被抢占
系统服务无法及时响应:系统服务都是Binder机制(16个线程),服务能力也是有限的,service的连接达到上限无法和系统服务通信
其它应用占用大量内存
3、ANR日志分析
当App发生ANR时,系统会dump出一个traces文件,目录/data/anr
bestchanggedembp:~ sun$ adb shell
HWJER:/ $ cd data/anr
HWJER:/data/anr $ ls -a
. anr_2022-02-15-12-48-17-616 dumptrace_ZlVIkw
.. anr_2022-02-15-16-34-22-122
HWJER:/data/anr $ exit
bestchanggedembp:~ sun$ adb pull data/anr/anr_2022-02-15-16-34-22-122
adb: error: failed to copy 'data/anr/anr_2022-02-15-16-34-22-122' to './anr_2022-02-15-16-34-22-122': remote open failed: Permission denied
bestchanggedembp:~ sun$ adb bugreport
/data/user_de/0/com.android.shell/file.... 36.4 MB/s (10895407 bytes in 0.285s)
先查看是否存在traces(有的手机厂商会根据时间戳生成一个文件),pull失败则用adb bugreport
此命令会导出一个压缩包,解压后在FS/data/anr下可以看到traces文件了。
traces参数解读:
//"main"表示主线程调用栈 prio:线程优先级 tid:线程内部id Runnable:表线程状态Runnable
"main" prio=5 tid=1 Runnable
//group:线程所属的线程组 sCount:线程挂起次数 dsCount:用于调试的线程挂起次数 obj:当前线程关联的java线程对象 self:当前线程地址
| group="main" sCount=0 dsCount=0 flags=0 obj=0x73cebe98 self=0xb4000077f1e9ec00
//sysTid:线程真正意义上的tid nice:调度优先级 cgrp:进程所属的进程调度组 sched:调度策略 handle:函数处理地址
| sysTid=23569 nice=-10 cgrp=default sched=0/0 handle=0x77f34f14f8
//state:线程状态 schedstat:CPU调度时间统计 utm/stm:用户态/内核态的CPU时间(单位jiffies) core:该线程最后运动所在核 HZ:时钟频率
| state=? schedstat=( 0 0 0 ) utm=0 stm=0 core=0 HZ=100
//stack:线程栈的地址区间 stackSize:栈的大小
| stack=0x7fd8b58000-0x7fd8b5a000 stackSize=8192KB
//线程所持有的当前虚拟机的multex名称,及持有方式:shared held:共享锁;exclusive held:独占锁
| held mutexes= "mutator lock"(shared held)
//说明主线程在等待下条消息进入消息队列
at android.os.MessageQueue.nativePollOnce(Native method)
at android.os.MessageQueue.next(MessageQueue.java:374)
at android.os.Looper.loop(Looper.java:185)
at android.app.ActivityThread.main(ActivityThread.java:9032)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:614)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1129)
main线程的状态是cpp代码中定义的状态
状态 | 说明 |
---|---|
ZOMBIE | 线程死亡,终止运行 |
RUNNABLE/RUNNING | 线程可运行或正在运行 |
TIME_WAIT | 执行了带有超时参数的wait、sleep或join函数 |
MONITOR | 线程阻塞,等待获取对象锁 |
WAIT | 执行了无超时参数的wait函数 |
INITIALIZING | 新建,正在初始化,为其分配资源 |
STARTING | 新建,正在启动 |
NATIVE | 正在执行JNI本地函数 |
VMWAIT | 正在等待VM资源 |
SUSPENDED | 线程暂停,通常是由于GC或debug被暂停 |
案例整理中...