ANR 的类型及触发机制归纳总结

聊到 ANR,很多人的第一反应是「卡住了」、「超时了」。但 ANR 到底是怎么触发的?什么情况下才会弹窗?

我刚开始做 Android 开发时,也以为只要主线程干活慢就会 ANR。后来踩过几次坑才发现——ANR 有明确的触发门槛,不是随便一个延迟都算。Android 系统在特定场景下会给一个超时窗口,超了才算。

ANR 的五大类型

官方把 ANR 分成了几类,每一类的触发条件和超时时间都不一样。我个人习惯把它们记成下面这张表:

类型 超时时间 触发场景
Input Dispatch ANR 5 秒 按键、触摸等输入事件在 5s 内未分发到前台 Activity
Broadcast ANR 前台 10s / 后台 60s BroadcastReceiver 的 onReceive() 方法超时未返回
Service ANR 前台 20s / 后台 200s Service 的 onCreate、onStartCommand、onBind 等方法执行超时
ContentProvider ANR 10 秒 ContentProvider 的 query、insert、update、delete 超时;或 provider 进程首次启动耗时太长
其他(系统执行耗时) 不定(常见 5~20s) ProcessRecord 状态机阻塞、低内存导致 Watchdog 干预、文件 I/O 或 Binder 调用超长

嗯,这里要注意:前台和后台的阈值差距很大。我遇到过好几次线上数据明明没超前台 10s,却依然报了 ANR——一查才发现是后台广播的超时只有 60s,但客户写了个网络请求阻塞了 70s。所以一定要搞清楚你的组件运行在前台还是后台。

Input Dispatch ANR——最「疼」的那种

说白了就是用户点了屏幕,5 秒钟内你的 Activity 没响应。系统会认为应用已经死锁或无响应。

我在项目里碰到过最典型的场景:列表加载数据时,把整个网络请求和图片解码放到了主线程。用户快速滑动列表,点了某个 item,等了 6 秒还没跳转…… ANR 弹窗直接糊脸上。

想重现这种 ANR,可以试试在 Activity 的 onCreate 里加个 Thread.sleep(6000)。不过可别在生产代码里这么干——我记得有同事这么搞过,被测试小姐姐喷了一周。

Broadcast ANR——容易被忽略的「影子杀手」

BroadcastReceiver 的 onReceive() 默认运行在主线程。如果你的广播处理里做了耗时操作,比如数据库写入、SharedPreferences 大量写入、HTTP 请求——恭喜你,前台 10s、后台 60s 后 ANR 就会准时报到。

避坑指南:我曾经在接收系统开机广播时,直接在里面初始化了一堆第三方库,结果慢到超时。后来乖乖改成了启动一个 IntentService 来处理。记住:onReceive() 里只做轻量操作,或者用 goAsync() 配合子线程。

Service ANR——后台挖的「大坑」

Service 的 ANR 触发门槛比较宽,前台 20s,后台 200s。但正因为时间长,很多人反而忽视它。

我调试过一个案例:一个音乐 App 在后台 Service 里下载歌曲,同时用 MediaPlayer 播放,这两件事都没问题。但它的网络库用的是同步阻塞式 OkHttp,下载过程中遇到超时重试,总耗时超过 200s。系统直接杀了 Service 同时弹了 ANR。解决方法很简单——异步下载或者用 WorkManager 去跑。

ContentProvider ANR——冷门但致命

ContentProvider 的 ANR 分两种情况:

  • 对 Provider 的 CRUD 操作(query/insert/update/delete)超过 10s
  • Provider 进程首次启动时,调用其 onCreate() 超时(同样是 10s)

第二种情况我踩过坑:一个日历应用使用了自定义 Provider,在 onCreate 里做了加密数据库的首次打开(涉及大量解密计算),超过 10s 导致 App 一启动就 ANR。后来我把解密逻辑延迟到了第一次查询时,并加了进度提示。

其他类型的 ANR——系统层的「非常规事件」

除了上面四种,系统内部还有一些机制也会触发 ANR:

  • Watchdog 检测:system_server 的关键线程(比如 ActivityManager、WindowManager)如果阻塞超过 60s,Watchdog 会杀系统进程并触发 ANR
  • 低内存场景:LMK 频繁杀进程后,主线程无法及时创建新组件,超时报 ANR
  • Binder 调用堵死:主线程调了一个 Binder 服务,对方进程被占着几十秒没响应,也会算作 ANR

触发机制的核心逻辑

不管哪种类型,ANR 的触发流程都差不多:

  1. 系统组件(ActivityManagerService / WindowManagerService)发送一个超时消息
  2. 启动一个超时定时器(比如 InputDispatcher 里的 5s 闹钟)
  3. 组件在规定时间内完成执行,则取消定时器
  4. 超时消息到达时组件还没完成,系统就认定发生 ANR

核心口诀:系统给时间——执行完就没事——没执行完就弹窗。说白了就是「任务需要在限定时间内从主线程返回」。

调试小技巧:在 Android Studio 的 Logcat 里搜索 ANR in,点中一条 ANR 日志,看 CPU usage 和 Load 部分。如果 CPU 很空闲但主线程在 sleep,那基本就是代码问题;如果 CPU 接近 100%,可能是并发或锁冲突导致的。

类型与触发关系一览

下面这张图把 ANR 类型、超时阈值、典型触发原因串在一起,方便你在脑海里建立整体认知:

ANR 类型与触发机制 ANR 触发判断 Input Dispatch 耗时 5s 未分发 触摸/按键事件 Broadcast 前台 10s / 后台 60s onReceive 超时 Service 前台 20s / 后台 200s onCreate/StartCmd ContentProvider CRUD 超时 10s onCreate 超时 其他系统异常 Watchdog / LMK Binder 阻塞 触发机制共同流程 系统发送超时消息 → 启动定时器 → 组件执行完毕则取消 定时器到期未取消 → 弹出 ANR 对话框并 dump traces 排查时一定要区分ANR类型 不同的超时阈值决定了不同的复现策略 例如:前台广播10s vs 后台广播60s 经验总结:ANR 超时不一定是主线程卡顿 也可能是系统进程被占用或锁竞争导致的“假死”

注意:不要试图在生产环境用 Thread.sleep 主动制造 ANR 来测试。应使用官方提供的 StrictMode 或者打上 debug 日志来模拟耗时操作。我曾经见过有人在应用启动时故意 sleep 8 秒测试 ANR 弹窗,结果被系统直接判定为不良行为,上了 Google Play 限制名单。

好了,这一章咱们把 ANR 的类型和触发机制都过了一遍。记住这张图和表格,后面定位具体 ANR 问题时,你就能快速判断是哪种超时在作怪,然后对症下药。