一、ANR 报警:程序卡死的 "110" 呼叫
想象 Android 系统是一个繁忙的城市,各个应用程序就像忙碌的工厂。当一个工厂(应用)超过规定时间没响应(比如前台服务超过 20 秒没干完活),就会触发 "城市报警中心"AMS(ActivityManagerService)的警报系统。
java
scss
// AMS接到报警的第一反应
final void appNotResponding(ProcessRecord app, ...) {
// 记录报警时间和基本信息到"城市日志"EventLog
EventLog.writeEvent(EventLogTags.AM_ANR, app.pid, app.processName, ...);
// 组建"专案组":先把卡死工厂和关键部门加入调查名单
firstPids.add(app.pid); // 卡死的工厂
firstPids.add(MY_PID); // 城市管理中心system_server
for (ProcessRecord r : mLruProcesses) {
if (r.persistent) firstPids.add(r.pid); // 重要的"国企"进程
else lastPids.put(r.pid, true); // 其他"民营企业"进程
}
}
关键比喻:
- EventLog 相当于 110 接警记录,精确记录报警时间
- firstPids 是 "重点嫌疑名单",包括卡死应用和核心系统进程
- lastPids 是 "普通围观群众" 名单,后续按需调查
二、现场勘查:多团队协作收集证据
1. 组建 "证据收集小组"
当 AMS 决定调查后,会启动一个 "证据收集小组",专门负责获取各个进程的 "工作记录"(堆栈信息):
java
arduino
// 证据收集总负责人
public static File dumpStackTraces(...) {
String tracesPath = "/data/anr/traces.txt"; // 证据档案柜
File tracesFile = new File(tracesPath);
tracesFile.createNewFile(); // 清空旧档案,准备新记录
// 先调查"重点嫌疑名单"
for (int pid : firstPids) {
Process.sendSignal(pid, Process.SIGNAL_QUIT); // 给工厂发信号:交工作记录!
Thread.sleep(200); // 等200ms收报告
}
// 再调查"Native特种部门"(如显卡、媒体部门)
String[] nativeProcs = {"mediaserver", "surfaceflinger"};
int[] pids = Process.getPidsForCommands(nativeProcs);
for (int pid : pids) {
Debug.dumpNativeBacktraceToFile(pid, tracesPath); // 用特殊工具获取Native部门记录
}
// 最后调查"CPU高负荷工厂"(前5名)
for (ProcessCpuTracker.Stats stats : processCpuTracker.getWorkingStats()) {
if (lastPids.containsKey(stats.pid) && numProcs < 5) {
Process.sendSignal(stats.pid, Process.SIGNAL_QUIT); // 高负荷工厂也交记录
numProcs++;
}
}
}
2. Native 部门的特殊调查手段
当需要调查 Native 进程(如显卡驱动、媒体引擎)时,系统会派出 "特种调查员"debuggerd,通过 socket 发送专门的调查命令:
c
运行
arduino
// debugger.c中的Native调查流程
int dump_backtrace_to_file(pid_t tid, int fd) {
int sock_fd = make_dump_request(DEBUGGER_ACTION_DUMP_BACKTRACE, tid, 0);
// 通过socket向debuggerd发送"获取堆栈"命令
while (read(sock_fd, buffer, sizeof(buffer)) > 0) {
write(fd, buffer, n); // 把调查结果写入档案柜
}
}
关键比喻:
- SIGNAL_QUIT 信号相当于给 Java 工厂发 "交工作日志" 通知
- debuggerd 相当于特种调查队,专门处理 Native 部门的调查
- traces.txt 是 "案件档案柜",所有证据都汇总到这里
三、证据分析:CPU 使用率与现场报告
1. CPU"工作效率" 统计
在收集堆栈的同时,系统会统计各个工厂的 "工作效率"(CPU 使用率),看看是不是因为某个工厂太繁忙导致卡死:
java
scss
// 两次更新CPU统计,计算时间段内的效率
updateCpuStatsNow(); // 第一次记录
// 中间收集堆栈...
updateCpuStatsNow(); // 第二次记录
// 生成效率报告
String cpuInfo = mProcessCpuTracker.printCurrentState(anrTime);
String loadInfo = processCpuTracker.printCurrentLoad();
info.append("CPU负载: ").append(loadInfo);
info.append("进程效率: ").append(cpuInfo);
2. 证据归档:重要证据存入 "证据库"
所有收集到的堆栈和 CPU 报告,最终会被存入系统的 "证据库"dropbox,方便后续分析:
java
scss
// 把证据存入dropbox
addErrorToDropBox("anr", app, app.processName,
tracesFile, cpuInfo, ...);
关键比喻:
- CPU 统计相当于工厂的 "考勤记录",查看是否有超负荷工作
- dropbox 相当于 "中央证据库",所有重要案件证据都会存档
- 证据包括:卡死原因(Reason)、各工厂日志(堆栈)、工作效率(CPU)
四、结案流程:根据情况决定处理方式
1. 后台工厂:直接 "停业整顿"
如果是后台不重要的工厂(用户不关心的应用),系统会直接让它 "停业整顿"(杀死进程):
java
kotlin
// 后台且用户不关心的工厂,直接杀死
if (!showBackground && !app.isInterestingToUserLocked()) {
app.kill("bg anr", true);
return;
}
2. 前台工厂:弹出 "停业通知"
如果是前台重要工厂(用户正在使用的应用),系统会弹出 "停业通知"(ANR 对话框),让用户选择处理方式:
java
ini
// 准备通知内容
Message msg = Message.obtain();
msg.what = SHOW_NOT_RESPONDING_MSG;
msg.obj = map;
map.put("app", app);
// 发送给UI线程,显示ANR对话框
mUiHandler.sendMessage(msg);
五、破案总结:ANR 调查的完整流程
-
报警阶段:应用超时触发 AMS 报警,记录 EventLog
-
组建专案组:确定重点调查对象(firstPids、lastPids)
-
现场勘查:
- 给 Java 工厂发 SIGNAL_QUIT 获取工作日志(堆栈)
- 用 debuggerd 调查 Native 特种部门
- 调查 CPU 高负荷的前 5 个工厂
-
效率分析:统计各工厂 CPU 使用率,分析是否超负荷
-
证据归档:所有证据存入 dropbox,方便后续分析
-
结案处理:后台工厂直接关闭,前台工厂通知用户
关键技术点对比:
工厂类型 | 调查手段 | 对应命令 |
---|---|---|
Java 工厂 | 发送 SIGNAL_QUIT | kill -3 [pid] |
Native 工厂 | debuggerd 特殊调查 | debuggerd -b [pid] |
通过这个 "破案" 故事,我们可以理解 Android 系统在 ANR 发生时,如何像侦探一样收集现场证据,分析问题原因,并记录关键信息以便后续排查。整个过程的核心就是围绕 "收集各进程堆栈" 和 "分析 CPU 使用情况" 展开,最终将证据归档到 dropbox 供开发者分析。