1、ANR基础:到底什么是 ANR?

ANR,全称 Application Not Responding,就是「应用无响应」。说白了,你的 App 卡住了,系统觉得它「死了」,就会弹出一个对话框,问用户是要「等待」还是「关闭」。我在项目里见过不少新手,第一次遇到 ANR 时一脸懵,以为手机坏了。其实它背后有非常清晰的规则。

1.1 什么是 ANR?

当 Android 的主线程(UI 线程)被长时间阻塞,无法及时处理用户输入或系统消息时,系统就会判定这个应用处于无响应状态,进而触发 ANR。为什么主线程这么特殊?因为它承担了所有 UI 绘制、事件分发、生命周期回调等关键任务。你想想看,如果主线程在做一个耗时 10 秒的网络请求,用户点按钮就没反应了,系统当然不能忍。

核心原则: 所有可能耗时超过 5 秒的操作,都不该放在主线程。

1.2 ANR 的三种类型

根据触发场景不同,ANR 主要分三类。我当年排查第一个线上 ANR 时,就是靠这三类阈值定位的:

  1. 输入无响应(InputDispatcher) —— 用户触摸、按键等事件,系统分发后超时没有被处理。最常见的 ANR 类型。
  2. 广播超时(BroadcastReceiver) —— 广播接收器的 onReceive 方法执行时间过长。前台广播超时 10 秒,后台广播超时 60 秒。
  3. 服务超时(Service) —— 服务的生命周期方法(如 onCreate、onStartCommand)执行太久。前台服务 20 秒,后台服务 200 秒。

嗯,这里要注意:后台广播和服务的时间容忍度更大,但并不是说可以随便慢。我曾经在一个项目中,后台广播里做了数据库批量写入,结果直接导致 ANR。后来全改成了 WorkManager 异步处理。

1.3 ANR 阈值速查表

组件/场景 前台阈值 后台阈值
输入事件(InputEvent) 5s —
广播(BroadcastReceiver) 10s 60s
服务(Service) 20s 200s
ContentProvider 发布 10s —

为什么前台和后台阈值不同?因为前台应用直接影响用户体验,系统希望你快一点响应;后台应用可以稍微慢一些,但也不能无限拖。我建议你把这些阈值记在心里,写代码时脑子里时刻绷着这根弦。

1.4 核心知识图谱

下面这张图梳理了本章的脉络,方便你一眼看清 ANR 的基础结构:

ANR 基础知识体系 ANR 定义 输入无响应 (5s) 广播超时 (10s/60s) 服务超时 (20s/200s) 用户触摸/按键超时 5s 前台10s / 后台60s 前台20s / 后台200s ⚠️ 经验提示 主线程阻塞是万恶之源,耗时操作一律扔到子线程

1.5 避坑指南

  • 不要在主线程做 I/O —— 文件读写、数据库查询、网络请求,统统扔到子线程。我曾经见过同事直接在 Activity.onCreate 里做 8MB 的文件解析,结果 ANR 必现。
  • 广播接收器里别干重活 —— 就算后台广播有 60 秒,也不代表你可以放心做复杂计算。记得用 goAsync 或者转交给 IntentService 或 WorkManager。
  • 服务的启动要快 —— 如果服务启动时需要初始化大量资源,考虑在 onStartCommand 里启动一个子线程,或者用 JobScheduler 延迟处理。
  • 小心锁竞争 —— 主线程等待一个子线程持有的锁,也会触发 ANR。我在优化一个视频播放器时,就遇到过主线程等音视频解码锁导致 ANR 的情况。
切记: ANR 不像 Crash 那样会直接闪退,但用户会看到「应用无响应」对话框,体验极差。而且 Google Play 对 ANR 率有严格要求,高 ANR 率会直接影响应用评分和推荐。

1.6 小结

这一章我们搞清楚了三个核心点:

  • ANR 就是主线程阻塞引发的无响应现象
  • 它分输入、广播、服务三种类型,每种阈值不同
  • 记住阈值,写代码时就知道哪些操作不能放主线程