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
1.4 调用链核心概念:Stack Trace 与 “谁卡住了谁”
ANR 最终依赖主线程 Stack Trace 来定位阻塞点。调用链核心包括:
- 主线程堆栈:记录当前正在执行的方法栈,是分析阻塞的入口。
- 关联线程状态:Binder 线程池、AsyncTask 线程、渲染线程等可能间接阻塞主线程。
- 锁链:常见于 sync 块、ReentrantLock、binder 同步调用造成的死锁链条。
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 本质是信任链断裂的结果。InputDispatcher 设置计时器后,主线程若未在规定路程内返回“处理完毕”信号,系统即判定 ANR。因此后续的模块边界优化,本质是缩短主线程单次消息的处理路径,将不可控的跨模块调用推至后台线程,保持信任链一直有效。