前言
ANR 即 Applicatipon No Response,程序无响应。Android 系统设计了 ANR 机制,其目的是监控与其交互的组件(Activity 等)和用户交互(InputEvent)的超时情况。这样能够判断应用进程(主线程)是否存在卡死或响应过慢的问题
相比 Crash,ANR 问题存在原因复杂,不易定位的特点,本文主要包括以下内容
- ANR 工作流程
- 如何监控 ANR?
- 如何定位 ANR 原因?
ANR 工作流程
ANR 可能触发的时机有多种,通常可以分为以下几方面:
其基本原理其实 WatchDog 的思想,如果发出的事件,在一定时间内没有消费,则触发 ANR。
这里说一下总体流程,如下图所示:
- 发生 ANR 后,系统会采集许多进程数据,进行堆栈转储,以生成 ANR Trace文件。其中,第一个被采集的进程必定是发生 ANR 的进程。
- 系统会向这些应用进程发送 SIGQUIT 信号,这些应用进程收到信号后开始进行堆栈转储
- 应用进程 Dump 堆栈成功后通过 Socket 与系统进程通信写 Trace 文件
- 在 Trace 文件写入完成后,如果发生 ANR 的进程是前台进程则弹出 Dialog,否则则直接杀死进程
如何监控 ANR?
在了解了 ANR 的工作流程之后,我们该如何监控 ANR 的发生呢?
ANR WatchDog 检测思路
既然 ANR 的原因是输入在定时间内没有响应,那么我们很自然地想到,向主线程发送一个任务,如果一段时间内没有被执行的话,就认为发生了 ANR
这个思路主要有以下几个问题
- 不准确,超时条件不一定会导致 ANR,例如,5 秒超时只是在 TouchEvent 未被消耗时发生 ANR 的条件之一,而其他条件则不一定是 5 秒。
- 漏检测:如果超时时间定为 5 秒,去检测 TouchEvent 的 ANR 存在一定的漏检测的概率(周期不同步)。
ANR 信号监听思路
在上面介绍 ANR 总体流程时,我们注意到当 ANR 发生时会发送 SIGQUIT 信号,那么我们通过监听这一信号不就可以实现 ANR 监控了吗?事实上 XCrash与 Matrix 都是通过这种方式实现 ANR 监控的
在这里需要注意,默认情况下进程通过SignalCatcher
监听SIGQUIT
信号,进行堆栈转储生成 ANR Trace 文件。因此当我们监听SIGQUIT
信号后,需要重新向SignalCatcher
发送SIGQUIT
如果缺少重新向 SignalCatcher 发送 SIGQUIT 信号的步骤,Android System 管理服务(AMS)将一直等待 ANR 进程写入堆栈信息。直到超过20秒的超时时间,AMS 才会被迫中断,并继续后续流程。这将导致 ANR 弹窗的显示非常缓慢(因为超时时间为20秒),同时在 /data/anr 目录下也无法生成完整的 ANR Trace 文件。
误报情况处理
当监听到 SIGQUIT 信号时,不一定是发生了 ANR。
Matrix 的文档中提到了两种误报的情况:
- 比如可能是其它进程 ANR 了,发生 ANR 的进程不是唯一需要进行堆栈转储的进程。系统会收集许多其他进程进行堆栈转储,用于生成 ANR Trace 文件
- 厂商或者是开发者自己发送的
SIGQUIT
信号,发送SIGQUIT信号其实是很容易的一件事情
因此我们需要在监听到信号时再进行一次检查:在 ANR 弹窗前,会给发生 ANR 的进程标记一个 NOT_RESPONDING 的 flag,而这个 flag 我们可以通过 ActivityManager 来获取
private static boolean checkErrorState() {
try {
Application application = sApplication == null ? Matrix.with().getApplication() : sApplication;
ActivityManager am = (ActivityManager) application.getSystemService(Context.ACTIVITY_SERVICE);