Android 7系统异常问题排查(六)应用异常(上)—ANR机制全解
2026/8/6 3:59:41 网站建设 项目流程

系列目录:第一篇:异常机制全景图 | 第二篇:Kernel Panic 与系统重启 | 第三篇:Tombstone 机制 | 第四篇:System Server Watchdog | 第五篇:System Server 崩溃 | 第六篇:ANR 机制 | 第七篇:Java 层崩溃 | 第八篇:Trace 机制 | 第九篇:日志系统 | 第十篇:实战方法论


一、ANR 概述

你可能遇到过这些场景:

  • App 弹出"应用无响应"对话框,但不知道是主线程卡死还是 Binder 调用超时
  • 滑动界面时突然卡住,几秒后弹出 ANR 对话框
  • 后台广播接收器执行过久,系统判定 ANR

ANR(Application Not Responding)是 Android 系统在检测到应用主线程长时间无响应时,向用户展示的对话框。ANR 的底层机制是系统的超时检测——系统在某些关键操作上设置了一个时间上限,如果主线程在限定时间内没有完成操作,就触发 ANR。


二、ANR 分类与超时阈值

在 AOSP 7 中,ANR 分为四类,各有不同的超时阈值:

类型超时阈值检测组件触发场景
Input ANR5 秒InputDispatcher应用未及时处理输入事件(触摸/按键)
Broadcast ANR前台 10s / 后台 60sBroadcastQueue广播接收器onReceive()执行过久
Service ANR前台 20s / 后台 200sActiveServices服务onCreate()/onStartCommand()执行过久
ContentProvider ANR10 秒ActivityManagerServiceProvider 发布超时(较少见)

三、Input ANR —— 输入事件超时

3.1 InputDispatcher 的角色

Input ANR 的检测发生在 Native 层的InputDispatcher

源码路径frameworks/native/services/inputflinger/InputDispatcher.cpp

触摸事件流: InputReader(从驱动读事件) ↓ InputDispatcher(分发到目标窗口) ↓ 目标 App 主线程(处理事件) ↓ 处理完成 → finishInputEvent() → 确认回执

3.2 超时检测机制

源码路径frameworks/native/services/inputflinger/InputDispatcher.cpp

// 默认输入分发超时:5 秒constnsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT=5000*1000000LL;// 5 sec// InputDispatcher::dispatchOnce() 核心逻辑voidInputDispatcher::dispatchOnce(){// 从队列取出事件,发送给目标 App// 启动 5 秒超时计时器// 如果 App 在 5 秒内调用了 finishInputEvent()// → 正常完成,取消计时器// 如果 5 秒内未收到确认// → 触发 Input ANR// → 通知 AMS 进行 ANR 处理}

关键设计DEFAULT_INPUT_DISPATCHING_TIMEOUT是 5000ms(5 秒),这个值定义了 Input ANR 的超时阈值。

3.3 特殊机制:聚焦窗口优先

InputDispatcher 会区分"前台窗口事件"和"后台窗口事件":

  • 前台聚焦窗口未及时响应一定触发 ANR
  • 后台窗口未及时响应→ 不直接触发 ANR,而是将这些事件丢弃,等待窗口重新聚焦时重发

这也是为什么后台 App 卡死不一定会直接弹 ANR 的原因。


四、Broadcast ANR —— 广播超时

4.1 超时检测位置

源码路径frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

publicfinalclassBroadcastQueue{// ...// 超时常量定义在 AMS 中// static final int BROADCAST_FG_TIMEOUT = 10*1000; // 10秒// static final int BROADCAST_BG_TIMEOUT = 60*1000; // 60秒staticfinalintBROADCAST_TIMEOUT_MSG=ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG+1;finallongmTimeoutPeriod;// 由构造函数传入// ...}

4.2 超时检测流程

源码路径frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

publicfinalclassBroadcastQueue{// ...finalvoidprocessNextBroadcast(booleanfromMsg){synchronized(mService){// 遍历广播队列while(mParallelBroadcasts.size()>0||mOrderedBroadcasts.size()>0){// 取出一个广播接收者BroadcastRecordr=getNextBroadcast();// 设置超时检测if(r.receiver!=null){// 发送超时检测消息longtimeoutTime=r.receiverTime+mTimeoutPeriod;setBroadcastTimeoutLocked(timeoutTime);// 发送广播给接收者deliverToRegisteredReceiverLocked(...);}}}}finalvoidbroadcastTimeoutLocked(booleanfromMsg){// 如果广播还没有处理完成 → ANRif(!didProcess){// 触发 ANRmService.appNotResponding(...);// 重新调度超时检测setBroadcastTimeoutLocked(mTimeoutPeriod);}}}

关键设计:超时检测通过 Handler 延迟消息实现。BROADCAST_TIMEOUT_MSG在超时时间到达时触发broadcastTimeoutLocked(),如果此时广播仍未处理完成,就触发 ANR。

4.3 前台 vs 后台广播的超时差异

广播类型超时AOSP 7 判断条件
前台广播10 秒Intent.FLAG_RECEIVER_FOREGROUNDisFg
后台广播60 秒普通广播(默认)

源码路径frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

publicclassActivityManagerServiceextendsActivityManagerNative{// ...staticfinalintBROADCAST_FG_TIMEOUT=10*1000;// 10秒staticfinalintBROADCAST_BG_TIMEOUT=60*1000;// 60秒// 前台广播队列和后台广播队列分别使用不同超时mFgBroadcastQueue=newBroadcastQueue(this,mHandler,"foreground",BROADCAST_FG_TIMEOUT,false);mBgBroadcastQueue=newBroadcastQueue(this,mHandler,"background",BROADCAST_BG_TIMEOUT,true);}

关键设计:前台广播的超时要求更严格(10 秒),因为用户正在等待操作完成(如短信发送、网络切换等)。


五、Service ANR —— 服务超时

5.1 超时检测位置

源码路径frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

publicfinalclassActiveServices{// ...staticfinalintSERVICE_TIMEOUT=20*1000;// 20秒staticfinalintSERVICE_BACKGROUND_TIMEOUT=SERVICE_TIMEOUT*10;// 200秒// ...}

5.2 超时检测流程

源码路径frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

publicfinalclassActiveServices{// ...voidrealStartServiceLocked(ServiceRecordr,ProcessRecordapp){// 通知 App 进程创建 ServicebumpServiceExecutingLocked(r,"create");app.thread.scheduleCreateService(r,...);// 设置超时检测scheduleServiceTimeoutLocked(app);}voidscheduleServiceTimeoutLocked(ProcessRecordproc){Messagemsg=mAm.mHandler.obtainMessage(ActivityManagerService.SERVICE_TIMEOUT_MSG);msg.obj=proc;// 发送延迟消息// 前台服务:SERVICE_TIMEOUT (20s)// 后台服务:SERVICE_BACKGROUND_TIMEOUT (200s)mAm.mHandler.sendMessageDelayed(msg,proc.execServicesFg?SERVICE_TIMEOUT:SERVICE_BACKGROUND_TIMEOUT);}voidserviceTimeout(ProcessRecordproc){// 如果服务还没响应 → ANRif(proc.executingServices.size()>0){// 触发 ANRmAm.appNotResponding(proc,...);}}}

关键设计:Service ANR 的超时也是通过 Handler 延迟消息实现。SERVICE_TIMEOUT是 20 秒,SERVICE_BACKGROUND_TIMEOUT是 200 秒(10 倍)。

5.3 前台 vs 后台服务超时差异

服务类型超时触发条件
前台服务20 秒startForeground()调用的服务
后台服务200 秒普通startService()

六、ANR 触发后的处理流程

6.1 AMS.appNotResponding() —— 核心处理

源码路径frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

publicclassActivityManagerServiceextendsActivityManagerNativeimplementsWatchdog.Monitor,BatteryStatsImpl.BatteryCallback{// ...finalvoidappNotResponding(ProcessRecordproc,ActivityRecordactivity,...){// 1. 记录 ANR 日志Slog.e(TAG,"ANR in "+proc.processName);// 2. 收集 CPU 使用情况updateCpuStatsNow();// 3. dump 所有线程堆栈到 /data/anr/traces.txtFiletracesFile=ActivityManagerService.dumpStackTraces(true,firstPids,...);// 4. 记录到 DropBoxManageraddErrorToDropBox("anr",proc,...);// 5. 根据 ANR 类型决定是否弹对话框if(!isSilentAnr){// 弹出 ANR 对话框(前台应用)Messagemsg=mHandler.obtainMessage(SHOW_NOT_RESPONDING_UI_MSG);msg.obj=...;mHandler.sendMessage(msg);}// 6. 广播 ANR 事件(供调试工具监听)Intentintent=newIntent("android.intent.action.ANR");broadcastIntentLocked(...);}}

关键设计:ANR 处理的核心是dumpStackTraces()——通过向目标进程发送 SIGQUIT 信号,让各进程的 Signal Catcher 线程输出堆栈到/data/anr/traces.txt

6.2 /data/anr/traces.txt 的生成

dumpStackTraces() 流程: 1. 确定需要 dump 的进程: - 触发 ANR 的进程(优先) - 关键系统进程(system_server、surfaceflinger、mediaserver) - 其他可能有关系的进程 2. 向每个目标进程发送 SIGQUIT 信号 3. 各进程的 Signal Catcher 线程响应 SIGQUIT 4. 输出各线程的堆栈到 /data/anr/traces.txt

七、traces.txt 解读方法

7.1 文件结构

----- pid 1234 at 2024-01-01 12:00:00 ----- Cmd line: com.example.app Build fingerprint: 'Android/aosp_...' ABI: 'arm64' "main" prio=5 tid=1 Native | group="main" sCount=1 dsCount=0 obj=0x12c0e0a0 | sysTid=1234 nice=-2 cgrp=default sched=0/0 | state=S schedstat=( ... ) at android.os.BinderProxy.transactNative(Native Method) at android.os.BinderProxy.transact(Binder.java:456) at com.example.SomeService$Stub$Proxy.doWork(SomeService.java:100) at com.example.MainActivity.onCreate(MainActivity.java:50) ... ----- end 1234 -----

7.2 线程状态字典

状态含义定位线索
Native正在执行 Native 代码(JNI 或系统调用)可能是 Binder 调用、IO 等待
Runnable正在运行或等待 CPU检查是否有密集计算
Blocked等待获取对象锁死锁/锁竞争的重点排查对象
Waiting调用了Object.wait()检查条件变量是否被 notify
TimedWaiting调用了Thread.sleep()或带超时的wait()检查 sleep 时间是否合理
SleepingThread.sleep()主线程不应 sleep

7.3 主线程 Blocked 判断

traces.txt 中"主线程(tid=1)处于 Blocked 状态"是最典型的 ANR 根因:

"main" prio=5 tid=1 Blocked at com.example.Utils.expensiveOperation(Utils.java:50) - waiting to lock <0x12345678> held by "Binder:1234_2" tid=10 "Binder:1234_2" tid=10 Runnable at com.example.Utils.expensiveOperation(Utils.java:45) - locked <0x12345678>

解读:Binder 线程持有锁,主线程等待锁 → 主线程被阻塞 → ANR。


八、常见 ANR 根因与规避

8.1 主线程执行 IO 操作

// 错误做法:主线程读取文件@OverrideprotectedvoidonCreate(BundlesavedInstanceState){FileInputStreamfis=newFileInputStream("/sdcard/large_file.bin");fis.read(buffer);// 可能耗时几秒 → ANR!}

规避:使用AsyncTaskHandlerThreadIntentServiceRxJava等异步方案。

8.2 主线程 Binder 同步调用

// 错误做法:主线程同步调用跨进程服务@OverrideprotectedvoidonResume(){IRemoteServiceservice=getService();service.doHeavyWork();// 远程服务耗时长 → 主线程等待 → ANR!}

规避:将跨进程调用移到后台线程,或使用异步 Binder(oneway)。

8.3 BroadcastReceiver 中执行耗时操作

// 错误做法:广播接收器中执行耗时操作publicclassMyReceiverextendsBroadcastReceiver{publicvoidonReceive(Contextcontext,Intentintent){// 这个操作必须在 10s(前台)/ 60s(后台)内完成doNetworkRequest();// 耗时操作 → Broadcast ANR!}}

规避onReceive()中启动 Service 处理,自身快速返回。

8.4 死锁

// 经典死锁场景// 线程A: synchronized(objA) { synchronized(objB) { ... } }// 线程B: synchronized(objB) { synchronized(objA) { ... } }

规避:统一锁的获取顺序,使用java.util.concurrent的锁工具。

8.5 预防工具:StrictMode

// 开发阶段开启 StrictMode 检测if(BuildConfig.DEBUG){StrictMode.setThreadPolicy(newStrictMode.ThreadPolicy.Builder().detectDiskReads().detectDiskWrites().detectNetwork().penaltyLog().build());}

九、ANR 定位实战流程

1. 获取 traces.txt → adb pull /data/anr/traces.txt (如果现场已丢失,从 bugreport 中提取) 2. 定位主线程状态 → 搜索 "Cmd line: com.example.app" → 找到 "main" tid=1 的状态 3. 分析阻塞原因 ├─ Blocked → 找持有的锁和等待的锁 ├─ Native → 看 Binder 调用栈,找对端进程 ├─ Runnable → 看是否密集计算或等待 CPU └─ Waiting → 看 wait/join/sleep 调用 4. 结合 CPU 使用情况 → traces.txt 开头有各进程的 CPU 占用 → 判断是 CPU 不足还是逻辑阻塞 5. 查看 logcat 上下文 → logcat -d | grep "ANR in" → 查看 ANR 前后 1 分钟的日志

十、总结

  1. ANR 是主线程的"超时罚单":系统给了主线程明确的时间窗口,超时就触发。

  2. 四类 ANR 各有不同的检测者:InputDispatcher(5s)、BroadcastQueue(10s/60s)、ActiveServices(20s/200s)。

  3. 前台 vs 后台的超时差异巨大:前台 5-20s,后台 60-200s——主线程问题在前台更容易暴露。

  4. traces.txt 是 ANR 分析的"宝典":线程状态 + 堆栈 + CPU 使用,三位一体定位根因。

  5. 预防胜于治疗:StrictMode + 异步化 + 代码审查,在开发阶段就规避 ANR 风险。

下一篇将分析应用异常的另一面——APK Java 层崩溃。


本文基于 AOSP 7(Android Nougat)源码编写

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询