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线程主要包括如下:
-
Activity:onCreate(), onResume(), onDestroy(), onKeyDown(), onClick(),etc -
AsyncTask: onPreExecute(), onProgressUpdate(), onPostExecute(), onCancel,etc -
Mainthread handler: handleMessage(), post*(runnable r), etc -
other
参考网址
android ANR产生原因和解决办法【转】_第八个猴子的博客-CSDN博客
Android 系统稳定性 - ANR 解析_飞翔凡人的博客-CSDN博客
|