这篇文章涉及到的Android源码参考https://www.jianshu.com/p/8c402a0210d9。
public void addErrorToDropBox(String eventType,
ProcessRecord process, String processName, String activityShortComponentName,
String parentShortComponentName, ProcessRecord parentProcess,
String subject, final String report, final File dataFile,
final ApplicationErrorReport.CrashInfo crashInfo,
@Nullable Float loadingProgress, @Nullable IncrementalMetrics incrementalMetrics,
@Nullable UUID errorId)
addErrorToDropBox是一个方法,位于ActivityManagerService.java中,一共有13个参数。该方法的用途以及每个参数的含义可以参考方法注释:
/**
* Write a description of an error (crash, WTF, ANR) to the drop box.
* 说明这个函数的功能是将一个错误的描述写入到dropbox中
* @param eventType to include in the drop box tag ("crash", "wtf", etc.)
* 表示要将哪个事件类型(例如崩溃、WTF等)包括在dropbox的标签中
* @param process which caused the error, null means the system server
* 表示哪个进程导致了错误。如果为null,则表示是system_server进程
* @param activityShortComponentName which triggered the error, null if unknown
* 表示哪个activity触发了错误。如果未知,则为null
* @param parentShortComponentName activity related to the error, null if unknown
* 表示与错误相关的activity的父activity。如果未知,则为null
* @param parentProcess parent process
* 表示父进程
* @param subject line related to the error, null if absent
* 表示与错误相关的主题行。如果没有,则为null
* @param report in long form describing the error, null if absent
* 表示详细描述错误的报告。如果没有,则为null
* @param dataFile text file to include in the report, null if none
* 表示要包括在报告中的文本文件。如果没有,则为null
* @param crashInfo giving an application stack trace, null if absent
* 表示提供应用程序堆栈跟踪的崩溃信息。如果没有,则为null
* @param loadingProgress the loading progress of an installed package, range in [0, 1].
* 表示已安装包的加载进度,范围在[0, 1]之间
* @param incrementalMetrics metrics for apps installed on Incremental.
* 在Incremental上安装的应用程序的指标
* @param errorId a unique id to append to the dropbox headers.
* 要添加到dropbox头部的唯一ID
*/
那现在我们看看它的实现:
// 这个函数不应该获取ActivityManagerService的锁,否则可能会阻止watchdog重置系统
// NOTE -- this must never acquire the ActivityManagerService lock,
// otherwise the watchdog may be prevented from resetting the system.
// 检查dropbox服务是否已发布。如果没有,函数会立即返回
// Bail early if not published yet
if (ServiceManager.getService(Context.DROPBOX_SERVICE) == null) return;
final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class);
// 根据传入的process和eventType生成一个dropbox标签。然后检查这个标签是否在dropbox中启用。
// 如果没有启用,函数会立即返回
// Exit early if the dropbox isn't configured to accept this report type.
final String dropboxTag = processClass(process) + "_" + eventType;
if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return;
// 为了防止过于频繁地收集和记录日志,函数实现了一个速率限制机制。
// 它检查自上次记录同一类型的错误以来是否已经过了10秒。
// 如果是,则重置计数器并允许记录。如果不是,并且计数器已经达到5,则函数会立即返回。
// Rate-limit how often we're willing to do the heavy lifting below to
// collect and record logs; currently 5 logs per 10 second period per eventType.
final long now = SystemClock.elapsedRealtime();
synchronized (mErrorClusterRecords) {
long[] errRecord = mErrorClusterRecords.get(eventType);
if (errRecord == null) {
errRecord = new long[2]; // [0]: startTime, [1]: count
mErrorClusterRecords.put(eventType, errRecord);
}
if (now - errRecord[0] > 10 * DateUtils.SECOND_IN_MILLIS) {
errRecord[0] = now;
errRecord[1] = 1L;
} else {
if (errRecord[1]++ >= 5) return;
}
}
// 创建了一个StringBuilder对象sb
final StringBuilder sb = new StringBuilder(1024);
// 向sb中添加与进程相关的基本头部信息
appendDropBoxProcessHeaders(process, processName, sb);
// 当进程不为空,即当进程不是system_server的时候,添加是否在前台以及进程运行时间这两项信息到sb
if (process != null) {
// 前台为Yes,后台为No
sb.append("Foreground: ")
.append(process.isInterestingToUserLocked() ? "Yes" : "No")
.append("\n");
// 进程运行时间指的是进程绝对运行时间,包括系统休眠时间
if (process.getStartTime() > 0) {
long runtimeMillis = SystemClock.elapsedRealtime() - process.getStartTime();
sb.append("Process-Runtime: ").append(runtimeMillis).append("\n");
}
}
// 添加触发错误的Activity信息到sb
if (activityShortComponentName != null) {
sb.append("Activity: ").append(activityShortComponentName).append("\n");
}
// 添加与触发错误的Activity相关的信息:父进程的名称、父Activity的信息
if (parentShortComponentName != null) {
if (parentProcess != null && parentProcess.getPid() != process.getPid()) {
sb.append("Parent-Process: ").append(parentProcess.processName).append("\n");
}
if (!parentShortComponentName.equals(activityShortComponentName)) {
sb.append("Parent-Activity: ").append(parentShortComponentName).append("\n");
}
}
// 添加主题行信息到sb
if (subject != null) {
sb.append("Subject: ").append(subject).append("\n");
}
// 添加错误ID到sb
if (errorId != null) {
sb.append("ErrorId: ").append(errorId.toString()).append("\n");
}
// 添加版本编译相关的信息到sb
sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
// 添加是否正在进行Debug到sb
if (Debug.isDebuggerConnected()) {
sb.append("Debugger: Connected\n");
}
// 添加崩溃信息到sb
if (crashInfo != null && crashInfo.crashTag != null && !crashInfo.crashTag.isEmpty()) {
sb.append("Crash-Tag: ").append(crashInfo.crashTag).append("\n");
}
// 添加加载进度信息到sb
if (loadingProgress != null) {
sb.append("Loading-Progress: ").append(loadingProgress.floatValue()).append("\n");
}
// millisSinceOldestPendingRead 表示从系统中最旧的挂起读取操作开始到现在所经过的毫秒数。
// 这可以是一个度量,用于跟踪系统是否正在及时处理读取请求,或者是否有读取请求被挂起过长时间
if (incrementalMetrics != null) {
sb.append("Incremental: Yes").append("\n");
final long millisSinceOldestPendingRead =
incrementalMetrics.getMillisSinceOldestPendingRead();
if (millisSinceOldestPendingRead > 0) {
sb.append("Millis-Since-Oldest-Pending-Read: ").append(
millisSinceOldestPendingRead).append("\n");
}
}
sb.append("\n");
// 避免在IO上阻塞调用者,所以剩余的工作在一个单独的线程中完成。
// Do the rest in a worker thread to avoid blocking the caller on I/O
// 从此刻度开始,我们不应该访问AMS的内部数据结构
// (After this point, we shouldn't access AMS internal data structures.)
// 新线程的名称以"Error dump: " + dropboxTag组成
Thread worker = new Thread("Error dump: " + dropboxTag) {
@Override
public void run() {
// 如果存在,则将现有的report追加到名为sb中
if (report != null) {
sb.append(report);
}
// public static final String ERROR_LOGCAT_PREFIX = "logcat_for_";
String logcatSetting = Settings.Global.ERROR_LOGCAT_PREFIX + dropboxTag;
// public static final String MAX_ERROR_BYTES_PREFIX = "max_error_bytes_for_";
String maxBytesSetting = Settings.Global.MAX_ERROR_BYTES_PREFIX + dropboxTag;
// 查询系统设置的日志行数,默认为0行
int lines = Settings.Global.getInt(mContext.getContentResolver(), logcatSetting, 0);
// 查询系统设置的单个dropbox文件的大小,默认为192KB
// How many bytes to write into the dropbox log before truncating
// static final int DROPBOX_DEFAULT_MAX_SIZE = 192 * 1024;
int dropboxMaxSize = Settings.Global.getInt(
mContext.getContentResolver(), maxBytesSetting, DROPBOX_DEFAULT_MAX_SIZE);
// Assumes logcat entries average around 100 bytes; that's not perfect stack traces count
// as one line, but close enough for now.
// static final int RESERVED_BYTES_PER_LOGCAT_LINE = 100;
// 假定每行日志占用的字节数为RESERVED_BYTES_PER_LOGCAT_LINE= 100KB
// sb.length()可以理解为头信息的字节数,lines * RESERVED_BYTES_PER_LOGCAT_LINE 估算的日志字节数
// maxDataFileSize 为出去头信息和日志信息后,还可以分配的字节数
int maxDataFileSize = dropboxMaxSize - sb.length()
- lines * RESERVED_BYTES_PER_LOGCAT_LINE;
if (dataFile != null && maxDataFileSize > 0) {
try {
//对dataFile进行截取,保证最大为maxDataFileSize字节数。
sb.append(FileUtils.readTextFile(dataFile, maxDataFileSize,
"\n\n[[TRUNCATED]]"));
} catch (IOException e) {
Slog.e(TAG, "Error reading " + dataFile, e);
}
}
// 将crashInfo中的堆栈跟踪(如果存在)追加到sb
if (crashInfo != null && crashInfo.stackTrace != null) {
sb.append(crashInfo.stackTrace);
}
if (lines > 0) {
sb.append("\n");
// 读取logcat的最后lines行
// Merge several logcat streams, and take the last N lines
InputStreamReader input = null;
try {
java.lang.Process logcat = new ProcessBuilder(
"/system/bin/timeout", "-k", "15s", "10s",
"/system/bin/logcat", "-v", "threadtime", "-b", "events", "-b", "system",
"-b", "main", "-b", "crash", "-t", String.valueOf(lines))
.redirectErrorStream(true).start();
try { logcat.getOutputStream().close(); } catch (IOException e) {}
try { logcat.getErrorStream().close(); } catch (IOException e) {}
input = new InputStreamReader(logcat.getInputStream());
int num;
char[] buf = new char[8192];
while ((num = input.read(buf)) > 0) sb.append(buf, 0, num);
} catch (IOException e) {
Slog.e(TAG, "Error running logcat", e);
} finally {
if (input != null) try { input.close(); } catch (IOException e) {}
}
}
// 把sb信息发送给dropboxmanagerservice进行处理
dbox.addText(dropboxTag, sb.toString());
}
};
// 针对不同的情况,执行不同的调度。进程为null,比如process为system_server。
// process == null时考虑系统可能正处于不稳定状态,或者进程即将结束,
// 为了确保任务能够在系统关闭或进程退出之前得到及时且完整的执行,选择了同步的方式调用 worker.run() 而不是 worker.start()
// 这种设计可以降低任务丢失的风险.
if (process == null) {
// If process is null, we are being called from some internal code
// and may be about to die -- run this synchronously.
final int oldMask = StrictMode.allowThreadDiskWritesMask();
try {
worker.run();
} finally {
StrictMode.setThreadPolicyMask(oldMask);
}
} else {
worker.start();
}