一:什么是anr
ANR(Application Not Responding) 应用程序无响应。如果你应用程序在UI线程被阻塞太长时间,就会出现ANR,通常出现ANR,系统会弹出一个提示提示框,让用户知道。
二:anr 类型
1.KeyDispatchTimeout: input事件在5S内没有处理完成发生了ANR。
logcat日志关键字:Input event dispatching timed out
2.BroadcastTimeout
前台Broadcast:onReceiver在10S内没有处理完成发生ANR。
后台Broadcast:onReceiver在60s内没有处理完成发生ANR。
logcat日志关键字:Timeout of broadcast BroadcastRecord
3. ServiceTimeout
前台Service:onCreate,onStart,onBind等生命周期在20s内没有处理完成发生ANR。
后台Service:onCreate,onStart,onBind等生命周期在200s内没有处理完成发生ANR
logcat日志关键字:Timeout executing service
4.ContentProviderTimeout ContentProvider 在10S内没有处理完成发生ANR。
logcat日志关键字:timeout publishing content providers

三:anr 触发机制
ANR
是一套监控
Android
应用响应是否及时的机制,可以把发生
ANR
比作是引爆炸弹,那么整个流程包含三部分组成:
1. 启动
定时炸弹:中控系统
(system_server
进程
)
启动倒计时,在规定时间内如果目标
(
应用进程
)
没有干完所有的活,则中控系统会定向炸毁(
杀进程
)
目标。
2.
拆炸弹:在规定的时间内干完工地的所有活,并及时向中控系统报告完成,请求解除定时炸弹,则幸免于难。
3.
引爆炸弹:中控系统立即封装现场,抓取快照,搜集目标执行慢的罪证
(traces)
,便于后续的案件侦破
(
调试分析)
,最后是炸毁目标。
举例:用户点击按钮,中控系统启动定时炸弹; 点击按钮后,执行的事务,在规定的5s内完成了,则解除了定时炸弹;如果点击按钮,发现点击按钮做的事务在执行了5s,后还没有结束,然后又有别的输入事件,这个时候,就会引爆炸弹。
service超时机制
1.
客户端
(App
进程
)
向中控系统
(system_server
进程
)
发起启动服务的请求
2.
中控系统派出一名空闲的通信员
(binder_1
线程
)
接收该请求,紧接着向组件管家
(ActivityManager
线程
)
发送消 息,埋下定时炸弹
3.
通讯员
1
号
(binder_1)
通知工地
(service
所在进程
)
的通信员准备开始干活
4.
通讯员
3
号
(binder_3)
收到任务后转交给包工头
(main
主线程
)
,加入包工头的任务队列
(MessageQueue)
5.
包工头经过一番努力干完活
(
完成
service
启动的生命周期
)
,然后等待
SharedPreferences(
简称
SP)
的持久化;
6.
包工头在
SP
执行完成后,立刻向中控系统汇报工作已完成
7.
中控系统的通讯员
2
号
(binder_2)
收到包工头的完工汇报后,立刻去拆除炸弹。如果在炸弹定时结束前拆除炸弹 则拆除炸弹,否则会引发爆炸(
触发
ANR)
broadcast超时机制
对于静态注册的广播在超时检测过程需要检测
SP
,如下图所示
1.
客户端
(App
进程
)
向中控系统
(system_server
进程
)
发起发送广播的请求
2.
中控系统派出一名空闲的通信员
(binder_1)
接收该请求转交给组件管家
(ActivityManager
线程
)
3.
组件管家执行任务
(processNextBroadcast
方法
)
的过程埋下定时炸弹
4.
组件管家通知工地
(receiver
所在进程
)
的通信员准备开始干活
5.
通讯员
3
号
(binder_3)
收到任务后转交给包工头
(main
主线程
)
,加入包工头的任务队列
(MessageQueue)
6.
包工头经过一番努力干完活
(
完成
receiver
启动的生命周期
)
,发现当前进程还有
SP
正在执行写入文件的操作,便将向中控系统汇报的任务交给SP
工人
(queued-work-looper
线程
)
7. SP
工人历经艰辛终于完成
SP
数据的持久化工作,便可以向中控系统汇报工作完成
8.
中控系统的通讯员
2
号
(binder_2)
收到包工头的完工汇报后,立刻去拆除炸弹。如果在倒计时结束前拆除炸弹则炸弹拆除,否则会引发爆炸(
触发
ANR)。
SP
从
8.0
开始采用名叫
“queued-work-looper”
的
handler
线程,在老版本采用
newSingleThreadExecutor
创 建的单线程的线程池)
如果是动态广播,或者静态广播没有正在执行持久化操作的
SP
任务,则不需要经过
“queued-work-looper”
线程中 转,而是直接向中控系统汇报,流程更为简单,如下图所示:
provider超时机制
provider
的超时是在
provider
进程
首次启动的时候
才会检测,当
provider
进程已启动的场景,再次请求
provider
并不会触发provider
超时。
1.
客户端
(App
进程
)
向中控系统
(system_server
进程
)
发起获取内容提供者的请求
2.
中控系统派出一名空闲的通信员
(binder_1)
接收该请求,检测到内容提供者尚未启动,则先通过
zygote
孵化新进程
3.
新孵化的
provider
进程向中控系统注册自己的存在
4.
中控系统的通信员
2
号接收到该信息后,向组件管家
(ActivityManager
线程
)
发送消息,埋下炸弹
5.
通信员
2
号通知工地
(provider
进程
)
的通信员准备开始干活
6.
通讯员
4
号
(binder_4)
收到任务后转交给包工头
(main
主线程
)
,加入包工头的任务队列
(MessageQueue)
7.
包工头经过一番努力干完活
(
完成
provider
的安装工作
)
后向中控系统汇报工作已完成
8.
中控系统的通讯员
3
号
(binder_3)
收到包工头的完工汇报后,立刻去拆除炸弹。如果在倒计时结束前拆除炸弹则拆除炸弹,否则会引发爆炸(
触发
ANR).
input超时机制
input
的超时检测机制跟
service
、
broadcast
、
provider
截然不同,为了更好的理解
input
过程先来介绍两个重要线程的相关工作:
1.InputReader
线程: 负责通过
EventHub(
监听目录
/dev/input)
读取输入事件,一旦监听到输入事件则放入到InputDispatcher的
mInBoundQueue
队列,并通知其处理该事件;
2.InputDispatcher
线程: 负责将接收到的输入事件分发给目标应用窗口,分发过程使用到
3
个事件队列:
a.
mInBoundQueue
用于记录
InputReader
发送过来的输入事件;
b. outBoundQueue
用于记录即将分发给目标应用窗口的输入事件;
c. waitQueue
用于记录已分发给目标应用,且应用尚未处理完成的输入事件;
input的超时机制并非时间到了一定就会爆炸,而是处理再次有上报输入事件的过程才会去检测是否该爆炸,具体如下图所示。
四:为什么会出现ANR
1:主线程频繁进行耗时的IO操作:如数据库读写
2:多线程操作的死锁,主线程被block;(搜索关键字 held by)
3:主线程被Binder 对端block;
4:System Server中WatchDog出现ANR;
5:service binder的连接达到上线无法和和System Server通信
6:系统资源已耗尽(管道、CPU、IO)
五:ANR 实例分析
主要分析:通过logcat日志或者traces文件确认anr发生时间点; CPU使用率 ;主线程状态;其他线程状态
1,看anr日志,主要了解发生anr的进程等信息


2. 看主线程(下面是一个线程死锁的例子)


主线程执行了耗时操作





六,参数分析
main:main标识是主线程,如果是线程,那么命名成“Thread-X”的格式,x表示线程id,逐步递增。 prio:线程优先级,默认是5
tid:tid不是线程的id,是线程唯一标识ID
group:是线程组名称
sCount:该线程被挂起的次数
dsCount:是线程被调试器挂起的次数
obj:对象地址
self:该线程Native的地址
sysTid:是线程号(主线程的线程号和进程号相同)
nice:是线程的调度优先级
sched:分别标志了线程的调度策略和优先级
cgrp:调度归属组
handle:线程处理函数的地址。
state:是调度状态
schedstat:从 /proc/[pid]/task/[tid]/schedstat读出,三个值分别表示线程在cpu上执行的时间、线程的等待时间和线程执行的时间片长度,不支持这项信息的三个值都是0; utm:是线程用户态下使用的时间值(单位是jiffies)
stm:是内核态下的调度时间值
core:是最后执行这个线程的cpu核的序号
七:分析步骤
1).收集的信息如下
前台
ANR
发生后,系统会马上去抓取现场的信息,用于调试分析,收集的信息如下
:
a. 将am_anr信息输出到EventLog,也就是说ANR触发的时间点最接近的就是EventLog中输出的am_anr信 息
b. 收集以下重要进程的各个线程调用栈trace信息,保存在data/anr/traces.txt文件
b1. 当前发生
ANR
的进程,
system_server
进程以及所有
persistent
进程
b2. audioserver, cameraserver, mediaserver, surfaceflflinger
等重要的
native
进程
b3. CPU
使用率排名前
5
的进程
c. 将发生ANR的reason以及CPU使用情况信息输出到main log
c1. 将
traces
文件和
CPU
使用情况信息保存到
dropbox
,即
data/system/dropbox
目录
c2. 对用户可感知的进程则弹出
ANR
对话框告知用户,对用户不可感知的进程发生
ANR
则直接杀掉
2). 分析步骤
1.
定位发生
ANR
时间点
2.
查看
trace
信息
3.
分析是否有耗时的
message,binder
调用,锁的竞争,
CPU
资源的抢占
4.
结合具体的业务场景的上下文来分析