千家信息网

Android ANR的信息收集过程是什么

发表于:2026-11-04 作者:千家信息网编辑
千家信息网最后更新 2026年11月04日,本篇内容介绍了"Android ANR的信息收集过程是什么"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所
千家信息网最后更新 2026年11月04日Android ANR的信息收集过程是什么

本篇内容介绍了"Android ANR的信息收集过程是什么"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

一. ANR场景

无论是四大组件或者进程等只要发生ANR,最终都会调用AMS.appNotResponding()方法,下面从这个方法说起。

以下场景都会触发调用AMS.appNotResponding方法:

  • Service Timeout:比如前台服务在20s内未执行完成;

  • BroadcastQueue Timeout:比如前台广播在10s内未执行完成

  • InputDispatching Timeout: 输入事件分发超时5s,包括按键和触摸事件。

二. appNotResponding处理流程

1. AMS.appNotResponding

final void appNotResponding(ProcessRecord app, ActivityRecord activity, ActivityRecord parent, boolean aboveSystem, final String annotation) {    ...    updateCpuStatsNow(); //第一次 更新cpu统计信息    synchronized (this) {      //PowerManager.reboot() 会阻塞很长时间,因此忽略关机时的ANR      if (mShuttingDown) {          return;      } else if (app.notResponding) {          return;      } else if (app.crashing) {          return;      }      //记录ANR到EventLog      EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,              app.processName, app.info.flags, annotation);                    // 将当前进程添加到firstPids      firstPids.add(app.pid);      int parentPid = app.pid;            //将system_server进程添加到firstPids      if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID);            for (int i = mLruProcesses.size() - 1; i >= 0; i--) {          ProcessRecord r = mLruProcesses.get(i);          if (r != null && r.thread != null) {              int pid = r.pid;              if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {                  if (r.persistent) {                      firstPids.add(pid); //将persistent进程添加到firstPids                  } else {                      lastPids.put(pid, Boolean.TRUE); //其他进程添加到lastPids                  }              }          }      }    }        // 记录ANR输出到main log    StringBuilder info = new StringBuilder();    info.setLength(0);    info.append("ANR in ").append(app.processName);    if (activity != null && activity.shortComponentName != null) {        info.append(" (").append(activity.shortComponentName).append(")");    }    info.append("\n");    info.append("PID: ").append(app.pid).append("\n");    if (annotation != null) {        info.append("Reason: ").append(annotation).append("\n");    }    if (parent != null && parent != activity) {        info.append("Parent: ").append(parent.shortComponentName).append("\n");    }        //创建CPU tracker对象    final ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);    //输出traces信息【见小节2】    File tracesFile = dumpStackTraces(true, firstPids, processCpuTracker,             lastPids, NATIVE_STACKS_OF_INTEREST);                updateCpuStatsNow(); //第二次更新cpu统计信息    //记录当前各个进程的CPU使用情况    synchronized (mProcessCpuTracker) {        cpuInfo = mProcessCpuTracker.printCurrentState(anrTime);    }    //记录当前CPU负载情况    info.append(processCpuTracker.printCurrentLoad());    info.append(cpuInfo);    //记录从anr时间开始的Cpu使用情况    info.append(processCpuTracker.printCurrentState(anrTime));    //输出当前ANR的reason,以及CPU使用率、负载信息    Slog.e(TAG, info.toString());         //将traces文件 和 CPU使用率信息保存到dropbox,即data/system/dropbox目录    addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,            cpuInfo, tracesFile, null);    synchronized (this) {        ...        //后台ANR的情况, 则直接杀掉        if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) {            app.kill("bg anr", true);            return;        }        //设置app的ANR状态,病查询错误报告receiver        makeAppNotRespondingLocked(app,                activity != null ? activity.shortComponentName : null,                annotation != null ? "ANR " + annotation : "ANR",                info.toString());        //重命名trace文件        String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);        if (tracesPath != null && tracesPath.length() != 0) {            //traceRenameFile = "/data/anr/traces.txt"            File traceRenameFile = new File(tracesPath);            String newTracesPath;            int lpos = tracesPath.lastIndexOf (".");            if (-1 != lpos)                // 新的traces文件= /data/anr/traces_进程名_当前日期.txt                newTracesPath = tracesPath.substring (0, lpos) + "_" + app.processName + "_" + mTraceDateFormat.format(new Date()) + tracesPath.substring (lpos);            else                newTracesPath = tracesPath + "_" + app.processName;            traceRenameFile.renameTo(new File(newTracesPath));        }                        //弹出ANR对话框        Message msg = Message.obtain();        HashMap map = new HashMap();        msg.what = SHOW_NOT_RESPONDING_MSG;        msg.obj = map;        msg.arg1 = aboveSystem ? 1 : 0;        map.put("app", app);        if (activity != null) {            map.put("activity", activity);        }                //向ui线程发送,内容为SHOW_NOT_RESPONDING_MSG的消息        mUiHandler.sendMessage(msg);    }    }

当发生ANR时, 会按顺序依次执行:

  1. 输出ANR Reason信息到EventLog. 也就是说ANR触发的时间点最接近的就是EventLog中输出的am_anr信息;

  2. 收集并输出重要进程列表中的各个线程的traces信息,该方法较耗时; 【见小节2】

  3. 输出当前各个进程的CPU使用情况以及CPU负载情况;

  4. 将traces文件和 CPU使用情况信息保存到dropbox,即data/system/dropbox目录

  5. 根据进程类型,来决定直接后台杀掉,还是弹框告知用户.

ANR输出重要进程的traces信息,这些进程包含:

  • firstPids队列:第一个是ANR进程,第二个是system_server,剩余是所有persistent进程;

  • Native队列:是指/system/bin/目录的mediaserver,sdcard 以及surfaceflinger进程;

  • lastPids队列: 是指mLruProcesses中的不属于firstPids的所有进程。

2. AMS.dumpStackTraces

public static File dumpStackTraces(boolean clearTraces, ArrayList firstPids, ProcessCpuTracker processCpuTracker, SparseArray lastPids, String[] nativeProcs) {    //默认为 data/anr/traces.txt    String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);    if (tracesPath == null || tracesPath.length() == 0) {        return null;    }    File tracesFile = new File(tracesPath);    try {        //当clearTraces,则删除已存在的traces文件        if (clearTraces && tracesFile.exists()) tracesFile.delete();        //创建traces文件        tracesFile.createNewFile();        FileUtils.setPermissions(tracesFile.getPath(), 0666, -1, -1);    } catch (IOException e) {        return null;    }    //输出trace内容【见小节3】    dumpStackTraces(tracesPath, firstPids, processCpuTracker, lastPids, nativeProcs);    return tracesFile;}

这里会保证data/anr/traces.txt文件内容是全新的方式,而非追加。

3. AMS.dumpStackTraces

private static void dumpStackTraces(String tracesPath, ArrayList firstPids, ProcessCpuTracker processCpuTracker, SparseArray lastPids, String[] nativeProcs) {    FileObserver observer = new FileObserver(tracesPath, FileObserver.CLOSE_WRITE) {        @Override        public synchronized void onEvent(int event, String path) { notify(); }    };    try {        observer.startWatching();        //首先,获取最重要进程的stacks        if (firstPids != null) {            try {                int num = firstPids.size();                for (int i = 0; i < num; i++) {                    synchronized (observer) {                        //向目标进程发送signal来输出traces                        Process.sendSignal(firstPids.get(i), Process.SIGNAL_QUIT);                        observer.wait(200);  //等待直到写关闭,或者200ms超时                    }                }            } catch (InterruptedException e) {                Slog.wtf(TAG, e);            }        }        //下一步,获取native进程的stacks        if (nativeProcs != null) {            int[] pids = Process.getPidsForCommands(nativeProcs);            if (pids != null) {                for (int pid : pids) {                    //输出native进程的trace【见小节4】                    Debug.dumpNativeBacktraceToFile(pid, tracesPath);                }            }        }        if (processCpuTracker != null) {            processCpuTracker.init();            System.gc();            processCpuTracker.update();            synchronized (processCpuTracker) {                processCpuTracker.wait(500); //等待500ms            }            //测量CPU使用情况            processCpuTracker.update();            //从lastPids中选取CPU使用率 top 5的进程,输出这些进程的stacks            final int N = processCpuTracker.countWorkingStats();            int numProcs = 0;            for (int i=0; i= 0) {                    numProcs++;                    synchronized (observer) {                        Process.sendSignal(stats.pid, Process.SIGNAL_QUIT);                        observer.wait(200);                     }                }            }        }    } finally {        observer.stopWatching();    }}

该方法的主要功能,依次输出:

1.收集firstPids进程的stacks;

第一个是发生ANR进程;

第二个是system_server;

mLruProcesses中所有的persistent进程;

2.收集Native进程的stacks;(dumpNativeBacktraceToFile)

依次是mediaserver,sdcard,surfaceflinger进程;

3.收集lastPids进程的stacks;;

依次输出CPU使用率top 5的进程;

Tips: firstPids列表中的进程, 两个进程之间会休眠200ms, 可见persistent进程越多,则时间越长. top 5进程的traces过程中, 同样是间隔200ms, 另外进程使用情况的收集也是比较耗时.

4. dumpNativeBacktraceToFile

Debug.dumpNativeBacktraceToFile(pid, tracesPath)经过JNI调用如下方法:

static void android_os_Debug_dumpNativeBacktraceToFile(JNIEnv* env, jobject clazz, jint pid, jstring fileName) {    ...    const jchar* str = env->GetStringCritical(fileName, 0);    String8 fileName8;    if (str) {        fileName8 = String8(reinterpret_cast(str),                            env->GetStringLength(fileName));        env->ReleaseStringCritical(fileName, str);    }    //打开/data/anr/traces.txt    int fd = open(fileName8.string(), O_CREAT | O_WRONLY | O_NOFOLLOW, 0666);  /* -rw-rw-rw- */    ...        if (lseek(fd, 0, SEEK_END) < 0) {        fprintf(stderr, "lseek: %s\n", strerror(errno));    } else {        //【见小节5】        dump_backtrace_to_file(pid, fd);    }    close(fd);}

5. dump_backtrace_to_file

[-> debugger.c]

int dump_backtrace_to_file(pid_t tid, int fd) {    return dump_backtrace_to_file_timeout(tid, fd, 0);}int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs) {  //通过socket向服务端发送dump backtrace的请求  int sock_fd = make_dump_request(DEBUGGER_ACTION_DUMP_BACKTRACE, tid, timeout_secs);  if (sock_fd < 0) {    return -1;  }  int result = 0;  char buffer[1024];  ssize_t n;  //阻塞等待,从sock_fd中读取到服务端发送过来的数据,并写入buffer  while ((n = TEMP_FAILURE_RETRY(read(sock_fd, buffer, sizeof(buffer)))) > 0) {    //再将buffer数据输出到traces.txt文件    if (TEMP_FAILURE_RETRY(write(fd, buffer, n)) != n) {      result = -1;      break;    }  }  close(sock_fd);  return result;}

可见,这个过程主要是通过向debuggerd守护进程发送命令DEBUGGER_ACTION_DUMP_BACKTRACE, debuggerd收到该命令,在子进程中调用 dump_backtrace()来输出backtrace。

三. 总结

触发ANR时系统会输出关键信息:(这个较耗时,可能会有10s)

1.将am_anr信息,输出到EventLog.(ANR开始起点看EventLog)

2.获取重要进程trace信息,保存到/data/anr/traces.txt;(会先删除老的文件)

Java进程的traces;

Native进程的traces;

3.ANR reason以及CPU使用情况信息,输出到main log;

4.再将CPU使用情况和进程trace文件信息,再保存到/data/system/dropbox;

整个过程中进程Trace的输出是最为核心的环节,Java和Native进程采用不同的策略,如下:

进程类型trace命令描述
Javakill -3 [pid]不适用于Native进程
Nativedebuggerd -b [pid]也适用于Java进程

说明:kill -3命令需要虚拟机的支持,所以无法输出Native进程traces.而debuggerd -b [pid]也可用于Java进程,但信息量远没有kill -3多。 总之,ANR信息最为重要的是dropbox信息,比如system_server_anr。

重要节点:

  • 进程名:cat /proc/[pid]/cmdline

  • 线程名:cat /proc/[tid]/comm

  • Kernel栈:cat /proc/[tid]/stack

  • Native栈: 解析 /proc/[pid]/maps

"Android ANR的信息收集过程是什么"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

进程 输出 信息 情况 文件 过程 重要 方法 内容 小节 使用率 命令 时间 目录 线程 队列 事件 前台 后台 场景 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 广东万易互联网科技有限公司惠州分公司 互联网最新的科技 互联网最新的科技有限公司 九亚互联网科技 火传媒上海互联网科技 美国互联网前沿科技 阿里巴巴算科技企业还是互联网 江阴互联网智能科技产品进口 迪庆互联网科技招生 互联网行业科技股 互联网科技和法律行业 互联网科技创新的路径 威龙国际互联网科技公司 长沙互联网科技 互联网第三次科技革命特征 中国科技和美国互联网 恒生科技指数 中概互联网 恒生互联网科技趋势 高科技和互联网的现代国家 互联网科技快速发展优美句子 广东万易互联网科技有限公司惠州分公司 互联网属科技 金融科技创新监管互联网金融 远信互联网科技 鑫亿盛互联网科技 互联网科技公司有哪些 汉立互联网科技有限公司联系电话 中盈科技互联网信息有限公司 江苏互联网智能科技产品出厂价 深圳互联网科技园地址
0