ANR分析

1.什么是ANR
ANR即Application Not Responding,顾名思义就是应用程序无响应。在Android中,一般情况下,四大组件均是工作在主线程中的,Android中的Activity Manager和Window Manager会随时监控应用程序的响应情况,如果因为一些耗时操作(网络请求或者IO操作)造成主线程阻塞一定时间,那么系统就会显示ANR对话框提示用户对应的应用处于无响应状态。

2.为什么会发生ANR
一句话总结:主线程没有在规定的时间内,做完要做的事情,就会发生ANR。

Input事件超过5s没有被处理完
Service处理超时,前台20s,后台200s
BroadcastReceiver处理超时,前台10s,后台60s
ContentProvider执行超时10s
activity/broadcast/contentProvider超时(ActivityManagerService.java):
在这里插入图片描述

service超时(ActiveServices.java):

在这里插入图片描述

从发生的原因分:

主线程有耗时操作,如有复杂的layout布局,IO操作等;
被Binder对端block;
被子线程同步锁block;
Binder被占满导致主线程无法和SystemServer通信;
得不到系统资源(CPU/RAM/IO);
从进程的角度分:

问题出在当前进程;
主线程本身耗时,或者主线程的消息队列存在耗时操作;
主线程被本进程的其他子线程block;
问题出在远端进程(一般是binder call或socket等通信方式);

3.怎么解决ANR
3.1 导出trace文件
如果ANR发生,对应的应用会收到SIGQUIT异常终止信号,dalvik虚拟机就会自动在/data/anr/目录下生成trace.txt文件,这个文件记录了在发生ANR时刻系统各个线程的执行状态,通过adb pull命令将这个文件导出分析。

3.2 trace文件格式解析
导出trace文件后,可以看到类似于如下的文件内容:

说明了发生ANR的进程id、时间和进程名称。

  • tll:thread list lock
  • tsl:thread suspend lock
  • tscl:thread suspend count lock
  • ghl:gc heap lock
  • hwl:heap worker lock
  • hwll:heap worker list lock

说明了线程名称、线程的优先级、线程锁id和线程状态。

线程名称是启动线程的时候手动指明的,这里的main标识是主线程,是Android自动设定的一个线程名称,如果是自己手动创建的线程,一般会被命名成“Thread-xx”的格式,其中xx是线程id,它只增不减不会被复用;
注意这其中的tid不是线程的id,它是一个在Java虚拟机中用来实现线程锁的变量,随着线程的增减,这个变量的值是可能被复用的;

最后线程的状态还分为如下几种:

 

group:线程组名称;
sCount:此线程被挂起的次数;
dsCount:线程被调试器挂起的次数;当一个进程被调试后,sCount会重置为0,调试完毕后sCount会根据是否被正常挂起增长,但是dsCount不会被重置为0,所以dsCount也可以用来判断这个线程是否被调试过;
obj:表示这个线程的Java对象的地址;
self:表示这个线程本身的地址;

线程的调度信息:

  • sysTid:Linux下的内核线程id;
  • nice:线程的调度优先级;
  • sched:分别标志了线程的调度策略和优先级;
  • cgrp:调度属组;
  • handle:线程的处理函数地址;

线程当前上下文信息:

state:调度状态;
schedstat:从 /proc/[pid]/task/[tid]/schedstat读出,三个值分别表示线程在cpu上执行的时间、线程的等待时间和线程执行的时间片长度,有的android内核版本不支持这项信息,得到的三个值都是0;
utm:线程用户态下使用的时间值(单位是jiffies);
stm:内核态下的调度时间值;
core:最后执行这个线程的cpu核的序号;
3.3 trace文件分析

trace文件顶部的线程一般是ANR的元凶

这是一个简单的方法,但是大部分情况下都适用,可以通过这个方法来快速判断是否是自己的应用造成了本次ANR。但是,并不是trace文件包含的应用就一定是造成ANR的帮凶,应用出现在trace文件中,只能说明出现ANR的时候这个应用进程还活着,trace文件的顶部则是触发ANR的应用信息。因此,如果你的应用出现在了trace文件的顶部,那么很有可能是因为你的应用造成了ANR,否则是你的应用造成ANR的可能性不大,但是具体是不是还需要进一步分析。

注意死锁和等待

虽然说ANR一般情况是由于让主线程做了很多耗时的操作,但是死锁或者主线程等待也是ANR高发的原因。
案例一

虽然应用com.hhhh.core出现在了trace文件中,但是在ANR的时候它在通过IPCThread在进行进程间通信,可查看远端进程binder的block。

案例二 

 这种情况说明ANR发生于com.android.example应用中,而且指明了ANR发生时代码的执行位置,很有可能是我们应用程序的问题,之后就需要通过这个trace文件指明的路径来对代码进行排查。

案例三

binder_3正处于monitor状态,也就是它在等待一个synchronized块或者方法,但是目前这个monitor正在被tid=12的线程持有,所以造成了此线程被阻塞。

mAccountInfoFile->accounts.xml文件地址:data/system/sync
accounts.xml文件乱码,读取时加了同步锁导致anr

系统耗时分析方案
系统做了一些耗时分析的操作,Log里面有体现。

binder_sample
功能说明:监控每个进程的主线程的binder transaction的耗时情况, 当超过阈值时,则输出相应的目标调用信息,默认1000ms打开。
log格式:52004 binder_sample (descriptor|3),(method_num|1|5),(time|1|3),(blocking_package|3),(sample_percent|1|6)
log实例:2754 2754 I binder_sample: [android.app.IActivityManager,35,2900,android.process.media,5]
log解释:执行android.app.IActivityManager接口时,所对应方法code =35(即STOP_SERVICE_TRANSACTION),花费了2900ms,block在package为 android.process.media的地方。
dvm_lock_sample
功能说明:当某个线程等待lock的时间blocked超过阈值,则输出当前的持锁状态 ;
log格式: 20003 dvm_lock_sample (process|3),(main|1|5),(thread|3),(time|1|3),(file|3),(line|1|5),(ownerfile|3),(ownerline|1|5),(sample_percent|1|6)
log实例:dvm_lock_sample: [system_server,1,Binder_9,1500,ActivityManagerService.java,6403,-,1448,0]
log解释:当system_server进程中的Binder_9线程执行到ActivityManagerService.java的6403行代码时,一直在等待AMS锁, 但该锁被同一文件的1448行代码所持有, 从而导致Binder_9线程被阻塞1500ms.


ANR小结

搜索关键字:

【last ANR】
【InputDispatcher】
【wm_on_】
【ActivityManager: ANR in】

分析过程:

搜索关键字【VM TRACES AT LAST ANR】找到ANR的trace文件(该文件内容其实就在下面几行),通过关键字【“main”】查看主线程调用栈,如果调用栈中有我们APP包名调用的方法就能直接定位到是哪里的问题了
搜索关键字【ActivityManager: ANR in】,下一行【Reason】可以直接看到ANR产生的原因
然后看看【CPU usage】部分,看看哪个APP占用cpu比例最高,如果某个APP占用CPU超过100%,那很大概率是该APP引起的;如果CPU占用率很低,那可能是主线程阻塞了。
【TOTAL:】部分可以看出占用CUP的哪一类操作,比如【iowait】是指正在等待IO操作;
如果【Reason:】显示” Input dispatching timed out”,那么可以搜索【InputDispatcher】以及【wm_on_】关键字,根据ANR发生时间找到该时间前后打印的Log,比如【Reason: Input dispatching timed out (ActivityRecord{c88403a u0 com.android.settings/.homepage.SettingsHomepageActivity t78} does not have a focused window)】,经过排查就是该Activity在ANR发生之前就已经stop了,导致没有focused window去处理该input事件


ANR的常见类型

ANR一般有三种类型:

1:KeyDispatchTimeout(5 seconds) --主要类型

按键或触摸事件在特定时间内无响应

2:BroadcastTimeout(10 seconds)

BroadcastReceiver在特定时间内无法处理完成

3:ServiceTimeout(20 seconds) --小概率类型

Service在特定的时间内无法处理完成

三:KeyDispatchTimeout

Akey or touch event was not dispatched within the specified time(按键或触摸事件在特定时间内无响应)

具体的超时时间的定义在framework下的

ActivityManagerService.java

//How long we wait until we timeout on key dispatching.

staticfinal int KEY_DISPATCHING_TIMEOUT = 5*1000

四:为什么会超时呢?

超时时间的计数一般是从按键分发给app开始。超时的原因一般有两种:

(1)当前的事件没有机会得到处理(即UI线程正在处理前一个事件,没有及时的完成或者looper被某种原因阻塞住了)

(2)当前的事件正在处理,但没有及时完成

五:如何避免KeyDispatchTimeout

1:UI线程尽量只做跟UI相关的工作

2:耗时的工作(比如数据库操作,I/O,连接网络或者别的有可能阻碍UI线程的操作)把它放入单独的线程处理

3:尽量用Handler来处理UIthread和别的thread之间的交互

六:UI线程

说了那么多的UI线程,那么哪些属于UI线程呢?

UI线程主要包括如下:

  1. Activity:onCreate(), onResume(), onDestroy(), onKeyDown(), onClick(),etc

  2. AsyncTask: onPreExecute(), onProgressUpdate(), onPostExecute(), onCancel,etc

  3. Mainthread handler: handleMessage(), post*(runnable r), etc

  4. other

参考网址

android ANR产生原因和解决办法【转】_第八个猴子的博客-CSDN博客

Android 系统稳定性 - ANR 解析_飞翔凡人的博客-CSDN博客

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值