第1章:ANR现象识别与场景分类

1.1 现象识别:用户与研发的“第一现场”

ANR(Application Not Responding,应用程序无响应) 是 Android 系统为保障用户交互流畅性而设立的一道“生死线”。当应用的主线程(UI 线程)被阻塞超过特定阈值时,系统会判定该应用已失去响应能力,并向用户弹出强制对话框。

ANR 核心判定逻辑:系统通过 InputManager、ActivityManager、BroadcastQueue 等核心服务定期“打桩”检测主线程是否处于空闲或阻塞状态。一旦检测到超时,立即触发 ANR 流程。

1.1.1 用户侧感知特征

  • 系统弹窗:屏幕中央出现“应用 X(无响应)”对话框,提供【等待】和【关闭应用】两个选项。这是最直接的可见特征。
  • 交互冻结:弹窗出现前,用户触摸屏幕无反馈,点击事件丢失,页面无法滑动或跳转。
  • 界面残影:部分情况下,对话框下方会透出应用页面的运行残影,但无法操作。

1.1.2 研发侧数据采集标志

  1. Logcat 日志:通过 adb logcat | grep "ANR in" 可直接捕获 ANR 发生时间点与进程名。
  2. Traces 文件:系统自动 dump 主线程及所有线程堆栈至 /data/anr/traces.txt(高版本可能按进程拆分)。
  3. EventLog 埋点:系统埋点 am_anr 会记录 ANR 类型、CPU 负载及等待锁信息。
# 采集 ANR 日志的典型命令
adb logcat -b events | grep "am_anr"
adb shell ls -l /data/anr/
adb pull /data/anr/traces.txt ./anr_debug/
排查技巧:除了 traces 文件,建议同时抓取 dumpsys dropbox --print data_app_anr 中的 dropbox 记录,其中包含 ANR 发生时的系统状态快照,用于交叉验证。

1.2 场景分类:四种 ANR 触发条件与判断规则

ANR 并非单一类型,根据触发组件的不同,系统拥有完全独立的超时监控器和处理逻辑。准确判断 ANR 类型是定位问题的第一步。

ANR 类型 触发组件 超时阈值 关键 Logcat 特征
输入超时 主线程 (UI 线程) 5 秒 Input event dispatching timed out
广播超时 BroadcastReceiver 前台 10s / 后台 60s Broadcast of Intent timed out
服务超时 Service 20 秒 Timeout executing service
Provider 超时 ContentProvider 10 秒 ContentProvider not responding

1.3 ANR 触发场景与系统响应流程

下图为 ANR 从发生到记录完成的完整故障响应链路,帮助开发者理解系统在不同超时场景下的处理方式。

ANR 触发场景与系统响应流程图 主线程(UI 线程)阻塞 输入事件超时(5s) 广播接收器超时 服务超时(20s) ContentProvider超时 系统弹出“应用无响应”对话框 生成 Traces & EventLog 故障树起点:通过 logcat 中的 ANR 类型标记,反向跟踪对应超时组件的执行链路

1.4 用户感知与埋点数据采集

仅凭用户反馈“手机卡了”无法精确定位。必须建立自动化数据采集机制,将用户感知转化为可量化的性能指标。

  • Dialog 展示埋点:在 AppNotRespondingDialog 弹出时,记录当前页面栈与用户操作轨迹。
  • 进程状态监控:通过 ActivityManager.getProcessMemoryInfo() 采集 ANR 发生时的 CPU 与内存水位。
  • 冷启动与后台唤醒:广播超时多发生在设备锁屏或低电量状态下,需结合 BatteryHistorian 分析电源干扰因素。
# 通过 logcat 解析 ANR 类型判断规则
# 若日志包含 "Input event dispatching timed out" → 输入超时
# 若日志包含 "Broadcast of Intent timed out" → 广播超时
# 若日志包含 "Timeout executing service" → 服务超时
# 若日志包含 "ContentProvider not responding" → Provider 超时
避坑指南:
1. 不要仅依赖用户截图中的“等待/关闭”对话框,这无法区分 ANR 类型。
2. 高版本 Android(11+)对后台广播与服务进行了严格的 BFS(Battery Freedom 策略)限制,导致后台超时阈值可能被系统动态缩短。
3. 输入超时往往与帧率卡顿(Jank)强相关,建议结合 FrameMetrics 一起分析。

1.5 预防性基础设施搭建

问题排查的最终目标是预防。在项目初期应建立以下机制,从根源减少 ANR 发生概率:

  1. 主线程监控:使用 Looper.getMainLooper().setMessageLogging() 监控主线程消息处理耗时。
  2. 异步改造清单:对 I/O 读取、网络请求、数据库操作、Bitmap 解码强制要求运行在子线程。
  3. 初始化优化:ContentProvider 的 onCreate() 中禁止执行耗时同步操作,支持懒加载或异步初始化。
总结:ANR 识别的第一性原理是“超时”与“主线程阻塞”。通过本章的分类排查表与流程图的故障树逻辑,开发者可以在海量日志中快速定位 ANR 的根因入口,为后续的堆栈分析与优化奠定基础。