ANR决策树一解决ANR问题思路小结
Wiki 和飞书中已经有很多 ANR 文章,此文章主要分享自己遇到 ANR 问题时的解决思路,会避免一些和其他人重复的内容。
一、怎么会引起 ANR 问题?
以 Service 的 onCreate 耗时造成 ANR举例:
- AMS 调用 App, 告诉 App 要执行 onCreate 方法了。
- App 开始执行 onCreate 方法。
3)App 执行完 onCreate 方法后告诉 AMS。
4)AMS 计算 1)到3) 之间的耗时,是否超过阈值,前台服务阈值 20s, 后台服务阈值 200s。

二、开始分析 ANR 问题的流程了
这里先强调一点,ANR 问题是一个耗时问题。
我们拿到的 trace 是一个时间片,时间片并不会引起 ANR。

什么问题会引起耗时?
等资源:包括 CPU 太高、内存占用太多、IPC(Binder)等待另外进程的回调、锁竞争、磁盘数据库IO、访问多媒体Wifi\Audio等硬件服务。
开始分析 ANR 问题了。
下图展示了 ANR 流程的决策树流程

2.1 蓝色快区域,focus window。
针对 input ANR,如果有 focus window 超过 5s 的 ANR。
Input 事件分发的三个队列:

InputDispatcher的实现主要涉及3个Queue:
(1). InboundQueue: 这个队列里面存储的是从InputReader送来到输入事件
(2). OutboundQueue:这个队列里面存储的是即将要发送给应用的输入事件
(3). WaitQueue:这个队列里面存储的是已经发给应用的事件,但应用还未处理完成。

Input 发生ANR的条件是:WaitQueue有元素,且mPendingEvent等待(加入OutboundQueue)超时。
(A)如果在dispatchTouchEvent, onTouchEvent等处于consumeEvents调用链中,执行耗时操作,即使未达到5s,也可能因为事件累计导致ANR。导致ANR的条件是在有PendingEvent时,WaitQueue中累积事件的总处理时长大于5s。
(B)如果在onButtonClicked等非consumeEvents调用链中,执行耗时操作,一定是大于5s(且有PendingEvent)才会触发ANR。因为onButtonClick是在dispatchTouchEvent执行完成后转抛到主线程的一条消息。也就是当进入onButtonClick函数时,点击的down/up事件已经执行完成,并从WaitQueue移除。(这样理解简单:consumeEvents调用链中有三个队列,onButtonClicked非consumeEvents调用链中四个队列。)参考:https://zhuanlan.zhihu.com/p/53331495
假如只有第一个事件耗时,即使耗时20s 或者耗时 30s 都不会引起 ANR,这是因为没有后续时间假加入的 PendingEvent,谷歌设计如此。大家可以写个demo验证一下。
2.2 绿色块区域,说明上图中的【6】DeadSystemException 问题:
binder 分为 oneway 调用和非 oneway 调用,
oneway 调用 buffer 为0.5M, 没有返回值。
非oneway 调用 buffer 为1M,有返回值。
上图 serviceDoneExecuting 是 oneway 接口,就是当 server 进程中的 binder buffer 超过 0.5M 的时候,App端执行Service 的 onCreate 方法后无法再通过 serviceDoneExecuting 告知 AMS,AMS 等待超过时间阈值后会抛出 ANR。
这之后会打印 DeadSystemException 的异常日志。
DeadSystemException 的相关 Wiki 可访问 DeadSystemException\DeadObjectException 问题案例汇总。
2.3 红色块 trace 日志
一般来说 trace 是一个时间片,无法确认是否在一个时间段内耗时。但是如果是在等锁,且有多处等锁。比如下面这种:
- waiting to lock <``0x0420f9fc``> (a com.android.server.wm.WindowManagerGlobalLock)``- waiting to lock <``0x0420f9fc``> (a com.android.server.wm.WindowManagerGlobalLock)``- waiting to lock <``0x0420f9fc``> (a com.android.server.wm.WindowManagerGlobalLock)``- waiting to lock <``0x0420f9fc``> (a com.android.server.wm.WindowManagerGlobalLock)
也可以从 trace 中等锁的线程的 trace 这个时间片推导处耗时,因为这个进程中多个其他线程也在等锁,可以近似认为多个时间片就是一个时间段。
2.4.1 橙色块 【12】
dvm_lock_sample,binder_sample,looper_sample… 这是在关键点插入的耗时统计(有的版本是 slow looper, slow binder 格式不一样,但是目的是一样的)。
只有这些日志,我们才好确定是时间段耗时。
2.4.2 橙色快 【13】
这是MIUI性能功耗定制的一些发热、加速、冻结策略,可能会和 ANR 相关,可以辅助分析。
2.5 什么时候可以放弃?
我自己的经验来说,经过上面 ANR 决策树的 13 个步骤,未得出有效结论,而且也不复现这时候可能需要放弃,标记不可复现,关jira。
但是有两点需要注意
(A)持续跟踪,后面是否还有这个应用 ANR 的情况,如果有就需要想办法解决,避免手机给用户带来不好的用户体验。
(B)如果有trace 和 log 不是很齐全的(决策树中的红色块和橙色块),可以联系功耗或者稳定性、性能同事添加日志。 复现后可以在接着分析。
三、ANR 具体流程
下面是 ContentProvider 的请求流程,ANR 主要监听紫色色块的地方。也是 AMS 通知 App 执行某个操作 App 执行完的时候再告知 AMS,AMS 统计这个时间段是否超过阈值,超过阈值则发生 ANR。

四种类型的 ANR 详细流程各不一样,这里不一一列举,
这里举例 ContentProvider 的 ANR 流程想说明如果对流程的细节了解的越清楚,解bug 时越有把握。
四、ANR 的案例
下面是 AMS 遇到的一些 ANR 案例
当你解过 1000 个 ANR 问题之后,就会发现没有什么 ANR 类型是没有遇到过的。