1 为什么会产生ANR
在Android里, App的响应能力是由Activity Manager和Window Manager系统服务来监控的. 通常在如下三种情况下会弹出ANR对话框:
1:5s内无法响应用户输入事件(例如键盘输入, 触摸屏幕等)
2:BroadcastReceiver在10s内无法结束
3:ServiceTimeout(20s) --小概率类型,Service在特定的时间内无法处理完成
造成以上两种情况的首要原因就是在主线程(UI线程)里面做了太多的阻塞耗时操作, 例如文件读写, 数据库读写, 网络查询等.
2 ANR分析
2.1 获取ANR产生的trace文件
ANR产生时, 系统会生成一个traces.txt的文件放在/data/anr/下. 可以通过[adb]命令将其导出到本地:
$adb pull data/anr/traces.txt .
本例中问题出现在MainActivity.java 27行,因为这里调用了Thread.sleep方法。
----- pid 30307 at 2018-09-06 14:51:14 -----
Cmd line: com.example.android
JNI: CheckJNI is off; workarounds are off; pins=0; globals=272
DALVIK THREADS:
(mutexes: tll=0 tsl=0 tscl=0 ghl=0)
"main" prio=5 tid=1 TIMED_WAIT
| group="main" sCount=1 dsCount=0 obj=0x416eaf18 self=0x416d8650
| sysTid=30307 nice=0 sched=0/0 cgrp=apps handle=1074565528
| state=S schedstat=( 0 0 0 ) utm=5 stm=4 core=3
| stack=0x7fdc4ca000-0x7fdc4cc000 stackSize=8MB
| held mutexes=
at java.lang.VMThread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:1044)
at java.lang.Thread.sleep(Thread.java:1026)
at com.example.android.MainActivity$1.run(MainActivity.java:27)
at android.app.Activity.runOnUiThread(Activity.java:4794)
at com.example.android.MainActivity.onResume(MainActivity.java:33)
at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1282)
at android.app.Activity.performResume(Activity.java:5405)
从第一行开始我们来按每一行解析其所代表的含义。
1,代表 PID at time
2,Cmd line: process name
3,固定头,指明下面都是当前运行的dvm thread ,“DALVIK THREADS:” 及所包含的线程数
4,"main" prio=5 tid=1 Native
分别代表thread name, java thread Priority, DVM thread id, DVM thread status
"main" :main thread -> activity thread
prio :java thread priority default is 5, (正常区域是1-10)
tid:是DVM thread id, 不是 linux thread id(下一行的sysTid才是)
Native:DVM thread Status 正常有这些状态(ZOMBIE, RUNNABLE, TIMED_WAIT, MONITOR, WAIT, INITALIZING,STARTING, NATIVE, VMWAIT, SUSPENDED,UNKNOWN)
5,group="main" sCount=1 dsCount=0 obj=0x416eaf18 self=0x416d8650
代表 DVM thread status。
group:是线程所处的线程组 default is “main”
sCount: 线程被正常挂起的次数 1 (thread suspend count)
dsCount: 线程因调试而挂起次数 0 (thread dbg suspend count)
obj: 当前线程所关联的java线程对象 0x75720fb8 (thread obj address)
Sef: 该线程本身的地址 0x7f7e8af800 (thread point address)
6,sysTid=30307 nice=0 sched=0/0 cgrp=apps handle=1074565528
代表Linux thread status显示线程调度信息
sysTId: linux系统下得本地线程id linux thread tid
Nice:线程的调度有优先级 linux thread nice value
cgrp: 优先组属 c group
sched: 调度策略 cgroup policy/gourp id
handle: 处理函数地址 handle address
7,state=S schedstat=( 0 0 0 ) utm=5 stm=4 core=3
代表CPU Sched stat 显示更多该线程当前上下文
State:调度状态 process/thread state (正常有 "R (running)", "S (sleeping)", "D (disk sleep)", "T (stopped)", "t (tracing stop)", "Z (zombie)", "X (dead)", "x (dead)", "K (wakekill)", "W (waking)",),通常一般的Process 处于的状态都是S (sleeping), 而如果一旦发现处于如D (disk sleep), T (stopped), Z (zombie) 等就要认真审查.
Schedstat (Run CPU Clock/ns, Wait CPU Clock/ns, Slice times) 该线程运行信息
utm: utime, user space time 线程用户态下使用的时间值(单位是jiffies)
stm: stime, kernel space time 内核态下得调度时间值
core: now running in cpu. 最后运行改线程的cup标识
8,stack=0x7f7dc93000-0x7f7dc95000 stackSize=1020KB
代表堆栈地址区域及size
9,held mutexes=
代表是否被锁住,正常有四个属性(mutexes: tll=0 tsl=0 tscl=0 ghl=0),0表示unlock,其它值都代表被lock,
tll: thread List Lock,
tsl: thread Suspend Lock,
tscl: thread Suspend Count Lock
ghl: gc Heap Lock
10,剩余的就是一些 Call Stack
2.2 CPU负荷和CPU使用率
//CPU前一分钟、五分钟、十五分钟的CPU平均负载,
//CPU平均负载可以理解为一段时间内正在使用和等待使用CPU的活动进程的平均数量。
Load: 5.16 / 9.69 / 30.66
//ago,表示ANR发生之前的一段时间内的CPU使用率,并不是某一时刻的值
CPU usage from 34388ms to -1ms ago:
4.1% 32614/com.qihoo.browser: 2.5% user + 1.6% kernel / faults: 465 minor 1 major
3% 379/adbd: 0.2% user + 2.8% kernel / faults: 1653 minor
2.6% 743/system_server: 1.8% user + 0.7% kernel / faults: 689 minor
2.5% 2326/com.qihoo.daemon: 2.2% user + 0.2% kernel / faults: 601 minor
2.4% 1009/RX_Thread: 0% user + 2.4% kernel
2% 280/surfaceflinger: 1.2% user + 0.8% kernel / faults: 1361 minor
1.8% 2675/com.lbe.security:service: 1.7% user + 0.1% kernel / faults: 749 minor
.......
+0% 3682/migration/1: 0% user + 0% kernel
+0% 3683/kworker/1:0: 0% user + 0% kernel
+0% 3684/ksoftirqd/1: 0% user + 0% kernel
9.7% TOTAL: 4.8% user + 4.2% kernel + 0.3% iowait + 0.3% softirq
//later,表示ANR发生之后
CPU usage from 1656ms to 2187ms later:
8.7% 743/system_server: 0% user + 8.7% kernel / faults: 4 minor
7% 943/InputDispatcher: 0% user + 7% kernel
1.7% 1199/Binder_6: 0% user + 1.7% kernel
5.2% 379/adbd: 0% user + 5.2% kernel / faults: 27 minor
3.5% 379/adbd: 0% user + 3.5% kernel
1.7% 1009/RX_Thread: 0% user + 1.7% kernel
1.7% 1768/mpdecision: 0% user + 1.7% kernel
1.7% 1784/mpdecision: 0% user + 1.7% kernel
1.2% 6883/kworker/u:37: 0% user + 1.2% kernel
1.3% 29165/kworker/0:2: 0% user + 1.3% kernel
2.3% TOTAL: 0.1% user + 0.3% kernel + 1.8% iowait
如果说某个进程在ANR发生时CPU使用率出现很高的情况,那么就可以知道这个进程在做非常消耗CPU的事情,一般这种情况下这个进程就是ANR进程,而消耗CPU的这个事情往往就是导致ANR的根源。
2.3 内存原因
内存原因有可能会导致ANR, 例如如果由于内存泄露, App可使用内存所剩无几.
// 以下trace信息来自网络, 用来做个示例
Cmdline: android.process.acore
DALVIK THREADS:
"main"prio=5 tid=3 VMWAIT
|group="main" sCount=1 dsCount=0 s=N obj=0x40026240self=0xbda8
| sysTid=1815 nice=0 sched=0/0 cgrp=unknownhandle=-1344001376
atdalvik.system.VMRuntime.trackExternalAllocation(NativeMethod)
atandroid.graphics.Bitmap.nativeCreate(Native Method)
atandroid.graphics.Bitmap.createBitmap(Bitmap.java:468)
atandroid.view.View.buildDrawingCache(View.java:6324)
atandroid.view.View.getDrawingCache(View.java:6178)
...
MEMINFO in pid 1360 [android.process.acore] **
native dalvik other total
size: 17036 23111 N/A 40147
allocated: 16484 20675 N/A 37159
free: 296 2436 N/A 2732
可以看到free的内存已所剩无几.
3 ANR的处理
三种不同的情况, 一般的处理情况如下
1.主线程阻塞
开辟单独的子线程来处理耗时阻塞事务.
2.CPU满负荷, I/O阻塞
I/O阻塞一般来说就是文件读写或数据库操作执行在主线程了, 也可以通过开辟子线程的方式异步执行.
3.内存不够用
增大VM内存, 使用largeHeap属性, 排查内存泄露等.
感谢
1.ANR分析思路简析-https://blog.csdn.net/sinat_34157462/article/details/78651870
2.Android应用ANR分析-https://www.jianshu.com/p/30c1a5ad63a3