ANR学习
一、ANR 概述
ANR 是 Android 系统用于监控应用是否及时响应的关键机制。形象地说,如同设置定时炸弹场景:系统的中控系统(system_server 进程)启动倒计时,若应用进程在规定时间内未完成特定任务,中控系统将触发 ANR,采取杀进程等措施;反之,若应用按时完成任务并向中控系统报告,炸弹则被拆除。触发 ANR 后,中控系统会封装现场、抓取快照(traces),以便后续排查问题根源。
二、四类超时机制
Service的ANR超时机制
图片流程解释
1.App进程向中控系统(system_server进程)发送启动服务请求
2.中控系统派发出一个通讯员(线程1)接收这个请求,然后转交给(ActivityManager),并安装定时炸弹
3.通讯员(线程1)通知目标阵营(Service进程)中的通讯员(线程3)让目标阵营开始做事
4.通讯员(线程3)将这个消息转达给做事的人(main),但是要排队(MessageQueue)。
5.经过努力,做事的人终于完成了事情(Service的启动的生命周期),然后开始等待SP持久化。
6.做事的人在做完事情后直接通知中控系统的负责接收的通信员(线程2),说明已经做完事情
7.通讯员(线程2)收到消息后,即刻拆除炸弹。如果此前操作超过时间阈值就来不及了。
SP持久化
它会将内存中的数据写入磁盘文件,并且会阻塞当前线程,直到数据成功写入或者写入失败。
与ANR关系
如果这个写入操作耗时过长,就会导致主线程被阻塞,无法及时处理其他消息和事件。当主线程阻塞时间超过系统设定的阈值(如用户输入事件 5 秒无响应、广播接收器 10 秒未处理完广播等),就会触发 ANR。相关函数
commit() 方法
这是一个同步操作。调用 commit() 后,系统会将数据写入内存中的映射结构,并且会阻塞当前线程,直到数据成功写入磁盘文件或者写入失败。它会返回一个布尔值,表示写入是否成功。apply() 方法
这是一个异步操作。调用 apply() 后,系统会将数据写入内存中的映射结构,并立即返回,不会阻塞当前线程。
然后,系统会在后台线程中异步地将数据写入磁盘文件。所以当你调用 apply() 后,不会立即完成持久化,可能需要等待一段时间。
通常推荐在主线程使用apply,因为异步执行不阻塞当前线程,但如果在一些特点场景下就不同了,比如下面的Broadcast的静态注册,它要求SP必须全部持久化到磁盘才能做其他的事情。在这种情况下如果过度使用apply会增大应用ANR的概率
Broadcast的ANR超时机制(静态注册)
图片流程解释
1.App进程向中控系统(system_server进程)发送广播请求
2.中控系统派发出一个通讯员(线程1)接收这个请求,然后转交给(ActivityManager)
3.(ActivityManager)来埋下定时炸弹
4.通讯员(线程1)通知目标阵营(Service进程)中的通讯员(线程3)让目标阵营开始做事
5.通讯员(线程3)将这个消息转达给做事的人(main),但是要排队(MessageQueue)。
6.经过努力,做事的人终于完成了事情(完成receiver启动的生命周期),但是SP正在执行文件操作,于是将汇报任务完成的工作交给了接盘侠(queued-work-looper线程)。
7.接盘侠终于完成了SP持久化的工作后,向中控系统汇报。
8.通讯员(线程2)收到消息后,即刻拆除炸弹。如果此前操作超过时间阈值就来不及了。
注意(如果是动态广播,或者静态广播没有正在执行持久化操作的SP任务,则不需要经过“queued-work-looper”线程中转,而是直接向中控系统汇报)
Provider的ANR超时机制
provider的超时是在provider进程首次启动的时候才会检测
图片流程解释
1.App进程向中控系统(system_server进程)发送内容提供者请求
2.中控系统派发出一个通讯员(线程1)接收这个请求,如果检测到内容提供者(provider),则先通过Zygote孵化进程
3.新孵化的provider进程向中控系统注册自己的存在
4.中控系统派发出一个通讯员(线程2)接收这个请求,然后转交给(ActivityManager),并安装定时炸弹
5.通讯员(线程2)通知目标阵营(Service进程)中的通讯员(线程4)让目标阵营开始做事
6.通讯员(线程3)将这个消息转达给做事的人(main),但是要排队(MessageQueue)。
7.经过努力,做事的人终于完成了事情(完成provider的安装工作)后直接向中控系统汇报事情已完成
8.通讯员(线程3)收到消息后,即刻拆除炸弹。如果此前操作超过时间阈值就来不及了。
Input的ANR超时机制
input变扫雷了
图片流程解释
1.Inputreader通过EventHub监听底层上报的输入事件,一旦收到输入事件就将其放到输入队列中,并唤醒InputDispatcher
2.InputReader会先检查是否有正在处理的事件,如果没有就从输入队列取出事件,并赋值给mPendingEvent,并且重置ANR的定时;否则不会取出事件,也不会重新定时。
然后检查窗口是否就绪,如果就绪条件满足其一就会进入扫雷(检查前一个正在处理的事件是否超时)
按键事件:输出队列和等待队列不为空
非按键:等待队列不为空,且等待队头超时500ms
如果满足就是终止,不满足就安全了进入3
3.将mPendingEvent转移到输出队列
4.当输出队列不为空,并且与应用通信管道连接正常,则从输出队列取出事件放到等待队列
5.inputdispatcher通过socket告知目标应用进程可以开始干活
6.App的做事的人(main)收到事件后,会转发到对应目标窗口
7.做事的人完成工作后,向中控系统汇报完成,中控系统会将事件从等待队列移除。
三、时间阈值
- Input:用户输入操作时,应用 5 秒内未处理输入事件;输入法弹出或隐藏等操作超过 5 秒未完成。
- Service:前台 Service 20 秒内未完成启动;后台 Service 200 秒内未完成启动;Service 的 onStartCommand 等方法执行耗时操作超相应时间限制。
- Broadcast:静态注册的广播接收器 10 秒内未完成广播处理;动态注册的广播接收器执行 onReceive 方法耗时超 10 秒;有序广播中前一接收器处理时间过长阻塞广播传递。
- Content Provider:query 操作因复杂查询、通信问题等超一定时间未返回结果;插入、更新、删除等操作因死锁、资源竞争等超系统设定时间阈值。10秒