第2章:Android ANR触发机制深入解析

2.1 ANR是什么?其实就是“系统等不下去了”

ANR全称 Application Not Responding。说白了,就是系统给你发了个信号:“你的App主线程卡住了,我不能再等你了。” 然后弹出一个让人崩溃的对话框——「XXX 无响应」。

我自己刚做Android时,一看到这个框就慌。后来才明白,ANR不是Bug,是系统的一种自我保护机制。它告诉你某个任务超时了。

那系统到底等多久才算“等不下去”?来看下面的核心时间阈值:

发生场景超时时长谁在监控
输入事件(按键/触摸)无响应5秒InputDispatcher
广播接收器 onReceive 未执行完前台10秒 / 后台60秒ActivityManagerService
Service 的 onCreate/onStartCommand 等生命周期20秒ActivityManagerService
ContentProvider 发布超时10秒ActivityManagerService
重点: 5秒!这是最常见的ANR触发时间。只要主线程在5秒内没处理完输入事件,系统就弹出ANR。

2.2 ANR触发流程——三步走

系统是怎么一步步判定ANR的?我画了一张流程图,你一看就懂:

(SVG图插入位置,见下方内联SVG)
ANR触发机制流程图 ANR触发机制流程图 步骤1:输入事件/广播/服务请求到达 步骤2:主线程开始处理(Handler阻塞) 超时检查 5秒内完成? 是 正常完成,无ANR 否(超时) 步骤3:触发ANR 系统生成traces.txt 弹出ANR对话框 说明:系统通过 ActivityManagerService 启动定时器,一旦超时即触发 ANR。 InputDispatcher 针对触摸事件使用独立的 5 秒超时检测。
记忆技巧: 前台输入事件5秒、前台广播10秒、Service 20秒。记住这三个数字,面试常问。

2.3 系统到底在监控什么?

ANR检测依赖两个关键角色:

  • InputDispatcher:专门盯着输入事件。从用户触摸到控件响应,如果5秒没动静,直接报ANR。
  • ActivityManagerService(AMS):监控广播、服务、ContentProvider这些组件生命周期。

我记得有一次排查一个问题:一个后台广播超时60秒才触发ANR。很多人以为是Bug,其实这就是设计——后台广播给的时间长,因为系统认为后台任务可以慢慢跑。但别高兴太早,系统一旦判定ANR,直接dump所有线程堆栈到 /data/anr/traces.txt。

2.4 实战案例:一个简单的ANR复现

光说理论不够,咱们写个demo试试。

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        findViewById(R.id.btn_block).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 在主线程上睡6秒 —— 超过5秒,必出ANR
                try {
                    Thread.sleep(6000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

跑起来,点一下按钮,等6秒。你会看到ANR弹窗。这时候去抓 traces.txt,会看到主线程卡在 Thread.sleep() 上。这就是最典型的ANR。

注意: 用 Thread.sleep() 模拟阻塞只是演示。生产环境里,真正的ANR元凶往往是:
- 数据库慢查询(比如一次查几千条记录)
- 网络请求同步调用
- 大量位图解码
- 锁竞争(死锁或者活锁)

2.5 不同组件超时差异一览

组件类型超时值超时后动作关键区别
Input事件5秒弹出ANR对话框只有主线程处理时就计时
BroadcastReceiver(前台)10秒弹出ANR对话框执行在onReceive中
BroadcastReceiver(后台)60秒弹出ANR对话框Android 8.0后后台广播限制更严
Service20秒弹出ANR对话框只对关键生命周期方法生效
ContentProvider10秒弹出ANR对话框发布超时,非查询超时

2.6 系统日志里怎么看ANR线索?

当ANR发生时,logcat会输出关键信息。我通常这样筛选:

// 快速定位ANR日志
adb logcat | grep -E "ANR|am_anr|InputDispatcher"

// 实际输出示例
ActivityManager: ANR in com.example.myapp (com.example.myapp/.MainActivity)
ActivityManager: PID: 12345
ActivityManager: Reason: Input dispatching timed out (Waiting to send key event because the focused window has not finished processing all of the input events...)
ActivityManager: Load: 2.15 / 1.82 / 1.67
CPU usage from 0ms to 5000ms later:
  ... 堆栈详情

这里有个坑:CPU负载高不一定代表ANR是因为CPU不够。有时候主线程光等锁,CPU很闲,也一样ANR。所以别只看load值,要看线程状态。

2.7 避坑指南:那些年我遇到的伪ANR

我曾经被一个低级错误坑过:用 SharedPreferences.apply() 大量写入,以为异步就没事。结果在低端机上,apply 会在主线程的写入队列里排队,一旦队列积压,主线程就被堵住了。嘿,这不就是ANR吗?

另外,不要在主线程做任何IO操作,包括写数据库、读写文件、加载大图。这些都是血泪教训。

核心原则: 主线程只做UI操作和轻量计算。所有耗时操作都扔到子线程或者使用协程/线程池。

2.8 小结:你该记住什么

  • ANR是超时机制,不是Bug,是系统保护。
  • 前台输入事件超时5秒,广播10/60秒,Service 20秒。
  • 检测由InputDispatcher和AMS完成。
  • 最重要的排查工具:/data/anr/traces.txt + logcat。
  • 主线程不要阻塞,其他都是废话。