1. ANR概念与分类

1.1 什么是ANR?

ANR,全称是 Application Not Responding。说白了,就是你的App卡死了,系统实在等不下去了,直接弹个对话框——"要关闭吗?"。

我记得刚入行那会儿,第一次见到这个对话框还以为是手机中毒了。后来才明白,这是Android系统在保护用户体验。系统规定了几种超时阈值,一旦主线程在规定时间内没有完成特定操作,立马触发ANR。

核心就一句话:主线程被阻塞了,而且阻塞时间超过了系统容忍的极限。

ANR的本质:主线程无法及时处理下一个消息,导致系统判定"无响应"。

为什么会这样?你想想看,主线程要同时处理用户点击、界面绘制、广播接收、Service启动……一旦某个任务占用了太久,后续所有任务都得排队。用户等得不耐烦了,系统也觉得不对劲,ANR就来了。

1.2 四大ANR类型与触发条件

ANR不是铁板一块,它分四种类型。每种类型的触发场景和超时阈值都不一样。我在项目里几乎都踩过一遍,一个一个跟你聊。

1.2.1 输入事件ANR

这是最常见的ANR类型。用户点了一下屏幕,系统把触摸事件分发给App,但App5秒内没有处理完成——直接弹窗。

触发条件:

  • 输入事件(按键、触摸、轨迹球)分发后,5秒内没有处理完
  • 或上一个输入事件处理超过5秒,导致后续事件被积压

我的经验:我遇到过一个案例,用户在列表快速滑动时触发大量异步回调,回调里直接更新UI,结果主线程被活活拖死。这就是典型的输入ANR。

1.2.2 广播ANR

广播分两种:前台广播和后台广播。阈值不一样,你别搞混了。

广播类型 超时阈值 典型场景
前台广播 10秒 高优先级、用户可感知的广播
后台广播 60秒 普通广播、系统广播

广播ANR的坑在哪里?很多人以为广播是异步的,不卡主线程。其实broadcast receiver的 onReceive() 方法默认就在主线程执行。你在里面做网络请求、数据库写入、或者循环解析JSON……嗯,10秒很快的。

避坑指南:我曾经在 onReceive() 里直接调了一个HTTP接口,结果网络延迟到8秒,前台广播直接就挂了。记住:广播里不要做耗时操作,用 goAsync() 或者扔给Service处理。

1.2.3 服务ANR

Service ANR有两个触发点:

  • 前台服务: onCreate()、onStartCommand()、onBind() 等生命周期方法超过20秒
  • 后台服务: 超过200秒

这里有个细节你注意一下:前台服务的20秒阈值,是从Android 8.0开始收紧的。早期版本是200秒统一处理。Google是铁了心要治后台滥用。

我有个血泪教训:项目里有一个上传日志的服务,每次启动时先把本地数据库里两个月的数据全读出来再压缩上传。结果一次上传前数据量爆炸,直接卡了30秒……前台服务ANR弹窗,用户直接卸载了App。

1.2.4 ContentProvider ANR

这一种相对少见,但一旦出现就很棘手。ContentProvider ANR发生在进程间通信时,比如一个App通过ContentResolver查询另一个App的ContentProvider,对方如果在规定时间内没有返回结果,查询方就会ANR。

触发条件:

  • ContentProvider的 query()、insert()、update()、delete() 方法超过30秒
  • 注意:这30秒是包含在Binder线程池里的,不是主线程,但如果调用方在主线程等待,照样卡死

实际场景:我遇到过三方SDK通过ContentProvider获取设备信息,结果那个Provider的实现里做了网络请求。查询方主线程调用,直接卡死5秒以上——虽然没到30秒阈值,但是又叠加了输入事件5秒阈值,照样ANR。

1.3 ANR知识体系总览

下面这张图把四种ANR类型和它们的核心参数画在了一起。你可以把它当作快速参考。

ANR知识体系总览 ANR核心概念 输入事件ANR 广播ANR 服务ANR ContentProvider ANR 触发阈值:5秒 场景:触摸、按键 主线程等待 最常见类型 前台10秒/后台60秒 onReceive()主线程 常用goAsync() 保活广播易触发 前台20秒/后台200秒 生命周期方法阻塞 IntentService安全 8.0后前台收紧 阈值:30秒 跨进程调用 Binder线程池处理 SDK集成时常见 核心思维:所有ANR的根本原因都是主线程阻塞 不要纠结于阈值数字,而要养成"主线程不做耗时操作"的肌肉记忆 —— 耗时操作扔给子线程 / 协程 / WorkManager

1.4 四种类型对比速查表

ANR类型 超时阈值 执行线程 常见触发场景
输入事件 5秒 主线程 UI更新阻塞、布局过于复杂、动画卡顿
广播(前台) 10秒 主线程(onReceive) 网络请求、数据库写入、JSON解析
广播(后台) 60秒 主线程(onReceive) 同上,但阈值宽松
服务(前台) 20秒 主线程(生命周期) 启动时加载大文件、加密运算
服务(后台) 200秒 主线程(生命周期) 同上,但阈值宽松
ContentProvider 30秒 Binder线程池 Provider实现耗时操作、网络请求

再一次强调:阈值只是"系统弹窗"的底线。用户体验的容忍度远小于这些数字。真实场景中,卡顿超过500毫秒用户就能感知,超过2秒就可能流失。别等ANR再动手,提前预防才是王道。

1.5 我的一点心得

ANR问题最难的不是修复,而是定位。尤其是线上ANR,复现条件千奇百怪。我最早遇到一个广播ANR,只在用户手机安装了很多应用并且低电量时才触发。后来发现是onReceive()里做了包名列表遍历——用户应用越多,耗时越长。

所以我把这个章节放在第一位,就是想让你先建立起"ANR识别"的直觉。遇到卡顿,第一时间想想:这属于哪种类型的ANR?阈值是多少?是不是在主线程?

有了这个基础,后面听定位工具、分析堆栈、解决方案的时候,才不会跑偏。