Android ANR 深度解析:从事件派发、监控机制到系统级排查

953 阅读4分钟

一句话总结:

ANR 就是“应用卡死弹窗”,主线程被耗时操作堵住了,用户等不及就崩溃!解决核心:别让主线程干重活,耗时任务全扔子线程,再用 Handler/协程切回来更新 UI。


一、ANR 的本质:主线程 Looper 的“交通大拥堵”

要理解 ANR,必须先理解 Android 的主线程消息循环机制。主线程(UI 线程)内部有一个 Looper,它不断地从 MessageQueue(消息队列)中取出消息(如点击、绘制、生命周期回调)并执行。

ANR 的根本原因:队列中某个消息(Message)的执行时间过长(例如一个耗时的网络请求),导致后续的所有消息都被阻塞,队列发生“交通拥堵”。系统无法处理新的用户输入或屏幕绘制事件,从而表现为界面卡死。


二、ANR 的“裁判员”:系统如何发现并报告拥堵?

ANR 并不是由应用自己检测的,而是由系统服务(运行在 system_server 进程中)作为“裁判员”来监控和触发的。

1. 输入事件 ANR (Input ANR) - 最常见的场景

  • 裁判员InputDispatcher 线程。

  • 监控机制

    1. 当用户触摸屏幕时,硬件中断将事件传递给 InputReader 线程,后者将其封装成输入事件。
    2. InputReader 将事件交给 InputDispatcher 线程,InputDispatcher 负责将事件派发给目标应用的窗口。
    3. 在派发前,InputDispatcher 会设置一个 5 秒的超时“闹钟”。
    4. 如果应用主线程因为正在处理其他耗时任务而未能及时消费掉这个输入事件(即Looper没机会处理它),InputDispatcher 的“闹钟”就会响起。
    5. InputDispatcher 会认为该应用无响应,并上报给 ActivityManagerService (AMS),最终由 AMS 弹出 ANR 对话框。

2. Service 及 BroadcastReceiver ANR

  • 裁判员ActivityManagerService (AMS)。

  • 监控机制

    • BroadcastReceiver:AMS 通过 Binder 将广播消息发送给应用进程的 ActivityThread 后,会在 ActiveServices 中为该广播设置一个 10 秒(前台应用)或 60 秒(后台应用)的超时。如果应用在规定时间内没有执行完 onReceive 方法,AMS 就会认定为 ANR。
    • Service:与广播类似,AMS 在处理 Service 的生命周期(如 onCreate, onStartCommand)时也会设置一个超时,前台 Service 为 20 秒,后台 Service 则长达 200 秒

三、ANR 的四大元凶与规避之道

所有 ANR 问题都可以归结为以下四类主线程阻塞,解决方案的核心都是将任务移出主线程

元凶类型具体场景推荐解决方案
1. CPU 密集型大量数据排序、复杂算法、JSON 解析、Bitmap 变换Kotlin 协程:使用 Dispatchers.Default 切换到计算密集型线程池。
2. I/O 阻塞网络请求、数据库读写、文件操作、SharedPreferences 读写Kotlin 协程:使用 Dispatchers.IO 切换到 I/O 密集型线程池。结合 Room、Retrofit 等库的 suspend 支持。
3. 锁竞争主线程等待一个被子线程持有的锁(synchronized1. 优化锁的粒度,减少锁的持有时间。
2. 使用读写锁 (ReentrantReadWriteLock) 等更高效的并发工具。
3. 审查代码,避免主线程参与任何可能产生等待的锁逻辑。
4. 进程间通信 (IPC) 阻塞主线程进行同步的 Binder 调用,而对端进程处理缓慢或发生死锁1. 总是假定对端会很慢,将所有 Binder 调用放在子线程。
2. 优先使用 oneway 关键字定义 AIDL 接口,使其成为异步调用。

四、从日志到内核:ANR 的现代化排查工具链

1. 开发期预防:StrictMode (严苛模式)

Application.onCreate 中开启,它能主动检测出主线程上的违规 I/O 和网络操作,并以日志或崩溃的形式报告,帮助你在开发阶段就扼杀 ANR 隐患。

2. 现场分析:/data/anr/traces.txt 文件

ANR 发生时,系统会生成此文件,记录下当时所有线程的堆栈信息。

  • 分析步骤

    1. adb pull /data/anr/traces.txt 导出文件。
    2. 在文件中搜索 Cmd line: com.your.package 找到你的应用信息。
    3. 重点关注 "main" 线程的堆栈,查看其状态(通常是 WAITINGBLOCKED)以及它被卡住的具体代码行。
    4. 同时查看其他线程是否持有了主线程正在等待的锁。

3. 系统级透视:Perfetto (新一代 systrace)

对于由 I/O 争用、CPU 调度问题或复杂的 Binder 调用引起的疑难 ANR,traces.txt 可能不足以定位问题。

  • 优势:Perfetto 可以抓取系统全局的 Trace 信息,让你清晰地看到在 ANR 发生前后,你的应用线程状态、CPU 核心的运行情况、内核的 I/O 事件等。
  • 使用场景:当 traces.txt 显示主线程 BLOCKED 在一个看起来无害的地方时,使用 Perfetto 可能会发现是内核正在忙于为其他进程处理 I/O,导致你的应用无法及时获得 CPU 时间片或 I/O 资源,这才是 ANR 的深层原因。