ANR 的阈值与超时机制归纳总结
说到 ANR,很多人第一反应是「不就是超时了嘛」。嗯,对也不对。
不同组件的超时时间完全不一样,检测机制也有细微差别。
我习惯把它们分成四类:Input 事件、Service、BroadcastReceiver 和 ContentProvider。
后来 Android 还加了 JobScheduler 的超时,但那个通常归到 Service 相关。
核心结论:ANR 的阈值不是写死的,不同版本、不同厂商可能有差异。但 AOSP 源码里的默认值,是咱们要掌握的基础。
各组件 ANR 阈值一览
下面这个表是我从 Android 源码里扒出来的,你直接拿去用:
| 组件类型 | 默认超时时间 | 触发条件 | 备注 |
|---|---|---|---|
| Input 事件 (Key/Dispatch) | 5 秒 | 主线程没能在 5 秒内处理完当前输入事件 | 最常见的一种,用户触摸卡顿时立马弹框 |
| Service | 20 秒 (前台) / 200 秒 (后台) | onStartCommand 或 onCreate 超时 | 前台服务阈值短,容易踩坑 |
| BroadcastReceiver | 10 秒 | onReceive 方法执行超过 10 秒 | 注意低内存时会更严格 |
| ContentProvider | 10 秒 | 创建 Provider 超时 | 一般发生在冷启动首次加载时 |
| JobScheduler | 几分钟~几十分钟 | onStartJob 回调超时 | 取决于 Job 的 deadline,系统会按需触发 |
我自己的经验:Input 5 秒看起来很长,实际上用户连点 3 下没反应就开始骂了。所以我建议把自定义监控的阈值设在 2 秒左右,提前预警。
超时机制到底怎么工作的?
说白了,系统给每个关键组件都装了一个「倒计时器」。
启动组件时开始计时,组件处理完就取消计时。
如果超时器先响了,系统就会标记这个进程为 ANR。
举个例子,Service 的流程是这样的:
// 伪代码,只看逻辑
class ActiveServices {
void realStartServiceLocked() {
// 启动倒计时
scheduleServiceTimeout(service);
// 执行真实启动
service.onStartCommand(intent, flags, startId);
}
void serviceDoneExecuting() {
// 取消倒计时
cancelServiceTimeout(service);
}
void serviceTimeout(ServiceRecord r) {
// 倒计时到 —— 触发 ANR
appNotResponding("Executing service " + r);
}
}
你看,逻辑很清晰。但问题出在哪?
我曾经遇到过一个场景:Service 里调了远程 Binder 方法,对方进程卡住了。
Service 本身没做耗时操作,但 Binder 调用是同步的,导致整个 Service 超时。
嗯,这个坑我后面专门讲。
不同场景的超时差异
- 前台 vs 后台:Service 前台 20 秒,后台 200 秒。系统对用户可见的进程要求更苛刻。
- 低内存情况:BroadcastReceiver 在低内存时可能 5 秒就炸。别问我怎么知道的……
- 多窗口&画中画:输入事件的超时可能会略微放宽,但仍然是 5 秒以内。
我曾经踩过的坑:有一次我在 ContentProvider 的 onCreate 里做了数据库迁移,数据量有几十万条。结果首屏就 ANR 了,用户还没看到界面。那时我才知道 Provider 的超时只有 10 秒。后来我把迁移挪到了异步线程,并且用 android:initOrder 控制顺序。
如何用日志定位超时来源?
系统发生 ANR 后,会在 /data/anr/traces.txt 里记录当时的堆栈。
你分析的时候重点看两行:
"main" prio=5 tid=1 Runnable
| group="main" sCount=0 dsCount=0 obj=0x768a0000 self=0x7f8000
| sysTid=12345 nice=0 cgroup=foreground
| schedstat=( 123456 7890 123 )
at com.example.MyApp.onCreate(MyApp.java:10)
...
找 main 线程的堆栈,看它卡在哪一行。通常就是耗时操作或者死锁。
不过要注意,某些低端机的 kernel 可能不生成 traces 文件。
我建议你配合 Logcat,过滤 ANR in 关键词,能把系统判定超时的时刻抓出来。
小技巧:你可以用 adb 命令手动模拟超时,验证自己的监控逻辑:
adb shell am broadcast -a android.intent.action.SLEEP --es duration 15000
(当然,你需要一个自定义 Receiver 接收这个广播并 sleep 15 秒)
一张图看懂 ANR 超时机制
我画了张流程图,把上面所有信息串起来:
看图窍门:从触发源开始,不同组件的超时阈值不一样,但最终都会汇聚到同一个超时检测逻辑。只要倒计时到了,系统就认为你 ANR 了。
总结几个关键点
- Input 事件 5 秒,是所有类型里最敏感的,因为它直接感知用户操作。
- Service 分前台后台,后台 200 秒看似很宽松,但系统可能会因为其他原因提前杀进程。
- BroadcastReceiver 和 ContentProvider 都是 10 秒,前者容易被低内存影响,后者容易在冷启动时触发。
- 真要规避 ANR,不是靠调整阈值,而是把耗时操作从主线程挪走。阈值只是最后的安全绳。
特别提醒:某些厂商(如华为、小米)可能会自己修改 ANR 阈值。我曾经在小米 6 上见到过 Input 超时 4 秒就弹框的情况。所以,千万不要迷信标准值,最好在你的目标设备上实际测试一下。