ANR基本分析

1.ANR介绍。

ANR全名Application Not Responding, 也就是"应用无响应". 当操作在一段时间内系统无法处理时, 系统层面会弹出上图那样的ANR对话框.

  • KeyDispatchTimeout 5s内无法响应用户输入事件(例如键盘输入, 触摸屏幕等).
  • BroadcastReceiver在10s/60s内无法结束(前后台广播)..
  • ServiceTimeout(20 seconds) (前台service时间)
  • ContentProvider anr时间由apk自身设定(少见)


2 。如何避免ANR

知道了ANR产生的原因, 那么想要避免ANR, 也就很简单了, 就一条规则:

不要在主线程(UI线程)里面做繁重的操作.

这里面实际上涉及到两个问题:

  1. 哪些地方是运行在主线程的?
  2. 不在主线程做, 在哪儿做?

主线程:Activity:onCreate(), onResume(), onDestroy(), onKeyDown(), onClick(),etc AsyncTask: onPreExecute(), onProgressUpdate(), onPostExecute(), onCancel,etc Mainthread handler: handleMessage(), **post***(runnable r), etc Other


耗时的工作(比如数据库操作,I/O,连接网络或者别的有 可能阻碍 UI 线程的操作)把它放入单独的线程处理. 比如打开wifi(因为跨进程操作,有可能wifiserver那边处理超时) 读写文件(操作是个iowait负载较大的行为,很容易anr) 查询语句(在数据库内容暴增之后,出现严重的性能问题,产生anr) SharedPreferences 的commit操作,本身是个等待操作,在我们activity退出时,有时保存当前状态,方便恢复,会使用commit,如果我们也有一个此时在操作,因为这个操作是有个锁,引起anr list的排序。(算法的质量,以及当列表数目激增后,是否能快速算完,是个耗时操作,会产生anr) bitmap的运算,(旋转,特效处理等) ThreadPoolExecutor 线程池,当我们从这里获取一个线程时候,如果此时所有线程都被使用,就只能迫使等待,此时会出现anr

sleep操作不会释放锁,也注意

3.分析步骤

1.找到ANR 位置

需要文件bugreport(trace信息 binderinfo信息 )  event log(am_anr) device log(main system log) kernel log(lowmemorykiller等等)

搜索关键字"am_anr"确认类型之类,搜索"ANR in"找到event log位置

2.看cpu信息,是否有特别高的进程,整体占用是否高,是否io高引起

进程高:可以通过搜索进程pid看在干什么,也可以通过trace文件看在做什么,通过pid tid

3.查看anr之前处于什么状态,有没有异常之类的

可以搜索event log anr的"pid tid"来看主线程在干什么,最后在做什么,有没有线索

device log搜索 pid pid看是否有异常情况

检测log看发生anr之前是否有异常情况,比如native crash ,out of memory之类

4.AMS在AppError.java中dump anr信息时,假如message history log那么就可以看到主线程卡了多久,卡在什么message(实现可以通过looper.java里实现)

5.查看trace

通过trace文件或者anr_history.txt,搜索cmd line: xxx或者pid  xxx来看是否能够对应时间,由于anr是ui线程卡,因此只需要看主线程(main)trace

4.可能原因

系统性能影响:

cpu usage高(比如kspxx名字进程,可能就与内存不足有关,usb进程那么可能是usb不兼容之类) 

low memory*(通过搜索kernel log对应时间点,lowmemorykiller关键字,一般kill adj<=2,那么系统就处于严重低内存状态,可能出现crash anr 系统缓慢 重启等问题)

主线程操作:IO文件访问  网络访问 thread.join  sleep SharedPreferences.commit等操作

5.trace log pattern(非常重要)

http://blog.csdn.net/ruingman/article/details/53118202
具体图案引用上述网站

//代表此线程是从另一个线程binder过来的,可以在binderinfo搜索30228:1612(pid:tid)
at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:112)  
at android.os.Binder.execTransact(Binder.java:453)


//看到BinderProxy.transactNative + IPCThreadState表示此线程binder了另一个线程,分析时需要看binderinfo transaction确认是那个线程问题
native: #05 pc 000198dd  /system/lib/libbinder.so (_ZN7android7BBinder8transactEjRKNS_6ParcelEPS1_j+60)
native: #06 pc 0001ec25  /system/lib/libbinder.so (_ZN7android14IPCThreadState14executeCommandEi+584)
native: #07 pc 0001ef73  /system/lib/libbinder.so (_ZN7android14IPCThreadState15waitForResponseEPNS_6ParcelEPi+262)
native: #08 pc 0001f049  /system/lib/libbinder.so (_ZN7android14IPCThreadState8transactEijRKNS_6ParcelEPS1_j+124)
native: #09 pc 00019fe3  /system/lib/libbinder.so (_ZN7android8BpBinder8transactEjRKNS_6ParcelEPS1_j+30)
native: #10 pc 0008a035  /system/lib/libandroid_runtime.so (???)
native: #11 pc 00d78919  /data/dalvik-cache/arm/system@[email protected] (Java_android_os_BinderProxy_transactNative__ILandroid_os_Parcel_2Landroid_os_Parcel_2I+140)
at android.os.BinderProxy.transactNative(Native method)
at android.os.BinderProxy.transact(Binder.java:505)

//binderinfo信息(一般在anr_history bugreport binderinfo.txt)此处表示pid 30228 tid1612的线程binder到15625:15635(pid15625 tid15635)
outgoing transaction 623807: edbbfa40 from 30228:1612 to 15625:15635 code 1 flags 10 pri 10 r1 node 222269 size 740:4 data fb600404

//下面这个15625:0表示pid 30228 tid1612的线程binder到15625,但是失败了,一般是因为15625 16个binder被占完了(system_server 32个binder),此时就需要通过trace看15625是被什么占的
outgoing transaction 623807: edbbfa40 from 30228:1612 to 15625:0 code 1 flags 10 pri 

//假如System_server中Block在此log表示,system_server 32binder沾满,没有可用binder
at android.os.Binder.blockUntilThreadAvailable(Native method)

//表示等锁0x0eeb54f1,这个锁是被tid=40持有()
  at com.android.server.MountService.getVolumeList(MountService.java:2759)
  - waiting to lock <0x0eeb54f1> (a java.lang.Object) held by thread 40

//"main"表示主线程 tid=1表示trace中的线程号,主线程为1  sysTid=22831表示系统tid,就是log中的线程号 utm=22 stm=22表示用户空间时间,可kernel空间时间,为22*10ms
 "main" prio=5 tid=1 Native
| group="main" sCount=1 dsCount=0 obj=0x73ee6470 self=0xb4d76500
| sysTid=22831 nice=0 cgrp=default sched=0/0 handle=0xb6f4bc00
| state=S schedstat=( 0 0 0 ) utm=22 stm=22 core=0 HZ=100


猜你喜欢

转载自blog.csdn.net/wd229047557/article/details/79509846