1. ANR基础与调用链概念

ANR(Application Not Responding)是 Android 系统对用户交互体验的“最后一道防线”。当应用主线程被阻塞超过预设阈值,系统便会终止交互并弹出 ANR 对话框。本章从边界、分层、组件职责、数据流、信任链路五个维度拆解 ANR 机制,为后续调用链分析与模块边界优化奠定基础。

1.1 边界定义:什么算 ANR?

ANR 不是所有超时,而是系统组件在规定时间内未完成关键操作。不同触发场景的超时阈值形成第一层边界:

触发场景超时阈值边界说明
Input 事件无响应5 秒应用未在 5s 内完成 onKeyDown / onTouchEvent
前台 Service 未启动20 秒Service.onCreate / onStartCommand 耗时超限
BroadcastReceiver 未返回10 秒(前台)
60 秒(后台)
onReceive 内同步操作挂起主线程
ContentProvider 未就绪10 秒Provider.onCreate 超时

⚠ 注意:系统 ANR 边界不等于业务感知边界。用户可能在系统弹出 ANR 前已经觉得卡顿,而某些高耗时操作(如 GCD 死锁)可能永不触发 ANR 阈值。边界清晰之后,才能区分“真 ANR”与“假阻塞”。

1.2 分层与组件职责

ANR 调用链横跨应用层 → Framework 层 → 内核层,每一层有明确的职责与信任契约:

  • 应用层(主线程):消费 Looper 消息(Handler、Input 事件、四大组件回调),必须在阈值内完成单次 dispatch。
  • Framework 层(ActivityManagerService、InputDispatcher):维护超时计数器,向应用发送信令,当超时到达时收集 trace 并触发 ANR。
  • 内核层(binder、信号、timefd):提供进程间通信与定时器机制,传递“主线程是否响应”的信号。

1.3 数据流 / 控制流:从用户点击到 ANR

以下是一个典型的 Input 事件 ANR 触发流程,体现了数据流(事件传递)与控制流(超时检测)的双向关系:

用户触摸屏幕
  │
  ├── InputReader (内核/dev/input) → 事件合成
  ├── InputDispatcher (system_server) → 向应用主线程发 MotionEvent
  │   ├── 1. 向 app 发送“上行”事件
  │   ├── 2. 启动 5s 超时计时器
  │   └── 3. 若主线程未在 5s 内调用 finished(),则……
  │
  └── App 主线程 (UI Thread) 处理:
       Handler.dispatchMessage() → View.onTouchEvent()
       若 Looper 阻塞 or 消息队列积压 → 超时未返回
  │
  └── InputDispatcher 检测超时 → 向 AMS 报告
       → AMS 生成 ANR 对话框 & 发 SIGQUIT 给 app 收集 trace
信任链路分析:Framework 信任应用主线程在 5s 内完成事件消费,并返回“处理完毕”信号。若信任链断裂(主线程卡死/Looper 退出),则 ANR 必然发生。

1.4 调用链核心概念:Stack Trace 与 “谁卡住了谁”

ANR 最终依赖主线程 Stack Trace 来定位阻塞点。调用链核心包括:

  • 主线程堆栈:记录当前正在执行的方法栈,是分析阻塞的入口。
  • 关联线程状态:Binder 线程池、AsyncTask 线程、渲染线程等可能间接阻塞主线程。
  • 锁链:常见于 sync 块、ReentrantLock、binder 同步调用造成的死锁链条。
风险点:仅凭 trace 可能无法复现完整锁链(如锁竞争发生在 trace 抓取前已释放)。需结合插桩、火焰图或系统事件日志(logcat)交叉验证。

1.5 选型权衡:Trace 采集方案对比

在 ANR 监控与归因时,不同调用链采集方案各有优劣:

方案优点缺点
系统 /data/anr/traces.txt零成本,系统自带延迟大,仅保存最后几次 ANR,无锁链分析
信号 SIGQUIT 自定义处理可定制 dump 内容(线程、锁、GC)需 root 或 daemon 进程,线上风险高
Looper 日志 + 卡顿监控实时性强,可对单次卡顿触发 trace额外性能开销,需权衡采集粒度
调用链预埋 + 后台聚合可还原锁链、消息队列积压开发成本高,需定义调用链 ID

1.6 落地步骤与验收标准

第一步:建立 ANR 基线
统计线上 ANR 发生次数、类型分布(Input / Service / Broadcast / Provider)、主线程卡顿时长(可通过 Choreographer 或 BlockCanary 模拟)。

第二步:引入轻量级调用链预采集
在关键入口(Handler.handleMessage、onTouchEvent、onReceive)埋入时间戳 + 线程栈快照(每 200ms 记录一次),确保 ANR 发生时至少保留 3 段栈快照。

第三步:分类并划定模块边界
将 ANR 按模块归属(UI 绘制、网络回调、数据库操作、业务逻辑),标注每个模块的平均耗时与超时概率。在后续章节中,我们将用“模块边界优化”方法切断跨模块阻塞。

验收标准

  • ANR 归因率 ≥ 85%(即从 trace 可明确判断阻塞模块)
  • 误报率(非主线程阻塞但被判定为 ANR)≤ 5%
  • 调用链采集对 UI 帧率的额外开销 ≤ 1ms/帧
ANR 触发调用链:分层架构与组件职责 用户触摸事件 Framework 层 InputDispatcher 应用层 主线程(Looper) 超时计时器 AMS ANR 处理 内核层 信任链路 Framework 信任主线程 在 5s 内返回 finished() 否则信任中断 → ANR

从本图可见:ANR 本质是信任链断裂的结果。InputDispatcher 设置计时器后,主线程若未在规定路程内返回“处理完毕”信号,系统即判定 ANR。因此后续的模块边界优化,本质是缩短主线程单次消息的处理路径,将不可控的跨模块调用推至后台线程,保持信任链一直有效。