addErrorToDropBox 源码分析

这篇文章涉及到的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();
        }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容