1.ANR介绍。
ANR全名Application Not Responding, 也就是"应用无响应". 当操作在一段时间内系统无法处理时, 系统层面会弹出上图那样的ANR对话框.
知道了ANR产生的原因, 那么想要避免ANR, 也就很简单了, 就一条规则:
不要在主线程(UI线程)里面做繁重的操作.
这里面实际上涉及到两个问题:
主线程: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
推荐这个博主对于Stability写的比较细:
https://www.jianshu.com/p/18f16aba79dd