深入理解Android Crash 流程

和你一起终身学习,这里是程序员Android

经典好文推荐,通过阅读本文,您将收获以下知识点:

一、Crash 概述
二、Crash处理流程
三、handleApplicationCrash处理分析
四、handleApplicationCrashInner 处理分析
五、APP Error info分析
六、makeAppCrashingLocked处理分析
七、startAppProblemLocked处理分析
八、stopFreezingAllLocked处理分析

九、 AppErrors.handleAppCrashLocked()
十、UIHandler
十一、 killProcess
十二、小结

Android 9.0 Crash 机制调用链

/frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
/frameworks/base/core/java/android/app/ActivityManagerNative.java (含内部类AMP)
/frameworks/base/core/java/android/app/ApplicationErrorReport.java

/frameworks/base/services/core/java/com/android/server/
    - am/ActivityManagerService.java
    - am/ProcessRecord.java
    - am/ActivityRecord.java
    - am/ActivityStackSupervisor.java
    - am/ActivityStack.java
    - am/ActivityRecord.java
    - am/BroadcastQueue.java
    - wm/WindowManagerService.java

/libcore/libart/src/main/java/java/lang/Thread.java

一、Crash 概述

App Crash (全称Application Crash), 对于Crash可分为Native Crash和 Framework Crash(包含app crash在内),对于Crash相信很多app开发者都会遇到,那么上层什么时候会出现Crash呢,系统又是如何处理Crash的呢。例如,在app大家经常使用try...catch语句,那么如果没有有效catch exception,就是导致应用Crash,发生没有catch exception,系统便会来进行捕获,并进入Crash流程。如果你是从事Android系统开发或者架构相关工作,或者遇到需要解系统性的疑难杂症,那么很有必要了解系统Crash处理流程,知其然还需知其所以然;如果你仅仅是App初级开发,可能本文并非很适合阅读,整个系统流程错综复杂。

在Android系统启动系列文章,已讲述过上层应用都是由Zygote fork孵化而来,分为system_server系统进程和各种应用进程,在这些进程创建之初会设置未捕获异常的处理器,当系统抛出未捕获的异常时,最终都交给异常处理器。

  • 对于system_server进程:system_server启动过程中由RuntimeInit.javacommonInit方法设置UncaughtHandler,用于处理未捕获异常;

  • 对于普通应用进程:进程创建过程中,同样会调用RuntimeInit.javacommonInit方法设置UncaughtHandler

1.1 crash调用链

crash流程的方法调用关系如下:

AMP.handleApplicationCrash
    AMS.handleApplicationCrash
        AMS.findAppProcess
        AMS.handleApplicationCrashInner
            AMS.addErrorToDropBox
            AMS.crashApplication
                AMS.makeAppCrashingLocked
                    AMS.startAppProblemLocked
                    ProcessRecord.stopFreezingAllLocked
                        ActivityRecord.stopFreezingScreenLocked
                            WMS.stopFreezingScreenLocked
                                WMS.stopFreezingDisplayLocked
                    AMS.handleAppCrashLocked
                mUiHandler.sendMessage(SHOW_ERROR_MSG)

Process.killProcess(Process.myPid());
System.exit(10);

二、Crash处理流程

RuntimeInit.java类的 main方法会调用commonInit()方法。

    public static final void main(String[] argv) {
        enableDdms();
        if (argv.length == 2 && argv[1].equals("application")) {
            if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application");
            redirectLogStreams();
        } else {
            if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool");
        }
        // AP Crash 处理流程初始化
        commonInit();

        // Native Crash  处理流程初始化
        nativeFinishInit();

        if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
    }

那么接下来以commonInit()方法为起点来展开说明。

1. RuntimeInit.commonInit

RuntimeInit.java

    protected static final void commonInit() {

        /*
         * set handlers; these apply to all threads in the VM. Apps can replace
         * the default handler, but not the pre handler.
         */
        LoggingHandler loggingHandler = new LoggingHandler();
        // app不能 替换 setUncaughtExceptionPreHandler
        Thread.setUncaughtExceptionPreHandler(loggingHandler);
        // 将异常处理器handler对象赋给Thread成员变量,
        Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
        ... ...

       }

接下来我们看看LoggingHandler的实现。LoggingHandler实现 Thread.UncaughtExceptionHandler 方法。

    private static class LoggingHandler implements Thread.UncaughtExceptionHandler {
        public volatile boolean mTriggered = false;

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            mTriggered = true;

            //保证crash处理过程不会重入
            if (mCrashing) return;
            //mApplicationObject等于null,一定不是普通的app进程. 
            //但是除了system进程, 也有可能是shell进程, 
            //即通过app_process + 命令参数 的方式创建的进程.
            if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) {            
                //系统 进程Crash打印的Log 信息
                /**
                       发生 系统Crash  时候可以搜索 关键字 FATAL EXCEPTION IN SYSTEM PROCESS
                **/
                Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
            } else {
                 /**
                       发生 APP Crash  时候可以搜索 关键字 FATAL EXCEPTION
                **/
                StringBuilder message = new StringBuilder();
                message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n");
                final String processName = ActivityThread.currentProcessName();
                if (processName != null) {
                    message.append("Process: ").append(processName).append(", ");
                }
                message.append("PID: ").append(Process.myPid());
                Clog_e(TAG, message.toString(), e);
            }
        }
    }
  • 1.当System进程Crash的信息:

开头 FATAL EXCEPTION IN SYSTEM PROCESS [线程名],接着输出Crash时的调用栈信息;

  • 2.当app进程Crash时的信息:

开头 FATAL EXCEPTION: [线程名],紧接着 Process: [进程名], PID: [进程id];最后输出发生Crash时的调用栈信息。

看到这里,你就会发现要从log中搜索Crash信息,只需要搜索关键词 FATAL EXCEPTION,即可查看出是那种异常;如果需要进一步筛选只搜索系统crash信息,则可以搜索的关键词可以有多样,比如 FATAL EXCEPTION IN SYSTEM PROCESS。

当输出完Crash信息到logcat里面,这只是Crash流程的刚开始阶段,接下来弹出Crash对话框,ActivityManagerNative.getDefault()返回的是ActivityManagerProxy(简称AMP)AMP经过binder调用最终交给ActivityManagerService(简称AMS)中相应的方法去处理,然后调用的是AMS.handleApplicationCrash()

分析完LoggingHandler后,我们继续看setDefaultUncaughtExceptionHandler(),它只是将异常处理器handler对象赋给Thread成员变量,即Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));。接下来看看KillApplicationHandler对象实例化过程。

2. KillApplicationHandler

RuntimeInit.java

KillApplicationHandler 实现 Thread.UncaughtExceptionHandler方法,主要处理由于未捕获的异常Crash导致APP 崩溃,运行在Main ThreadFramework 代码会捕获这些异常。

KillApplicationHandler 方法需要传递一个LoggingHandler的参数,
既 LoggingHandler loggingHandler = new LoggingHandler();LoggingHandler在上文已经分析过,接下来我们看看KillApplicationHandler方法.

KillApplicationHandler方法如下:

    private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
        private final LoggingHandler mLoggingHandler;
        public KillApplicationHandler(LoggingHandler loggingHandler) {
           // 构造方法,初始化 loggingHandler 
            this.mLoggingHandler = Objects.requireNonNull(loggingHandler);
        }

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            try {
                ensureLogging(t, e);

                // 保证crash处理过程不会重入
                if (mCrashing) return;
                mCrashing = true;
                ... ...
                //启动crash对话框,等待处理完成 【见小节2.1和3】
                ActivityManager.getService().handleApplicationCrash(
                        mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
            } catch (Throwable t2) {
                   ... ... 
            } finally {
               //确保当前进程彻底杀掉【见小节11】
                Process.killProcess(Process.myPid());
                System.exit(10);
            }
        }
      ... ...
}

接下来我们看看启动Crash弹窗的处理。new ApplicationErrorReport.ParcelableCrashInfo(e)方法。

2.1 ApplicationErrorReport.ParcelableCrashInfo

ApplicationErrorReport 主要用来描述 APP Error信息。
APP ERROR 信息分类如下:

  • TYPE_CRASH:APP Crash 信息

  • TYPE_ANR:APP ANR 信息

  • TYPE_BATTERY:Battery 使用信息

  • TYPE_RUNNING_SERVICE:正在运行的Service 相关信息

// 主要处理 APP Error 信息
public class ApplicationErrorReport implements Parcelable {

               ... ...
     public static class ParcelableCrashInfo extends CrashInfo implements Parcelable {

        //创建 CrashInfo 实例,初始化异常信息
        public ParcelableCrashInfo(Throwable tr) {
            super(tr);
        }
        ...  ... 
        public static final Parcelable.Creator<ParcelableCrashInfo> CREATOR =
                new Parcelable.Creator<ParcelableCrashInfo>() {
                    @Override
                    public ParcelableCrashInfo createFromParcel(Parcel in) {
                        return new ParcelableCrashInfo(in);
                    }

                    @Override
                    public ParcelableCrashInfo[] newArray(int size) {
                        return new ParcelableCrashInfo[size];
                    }
                };
    }
              ... ...
}

ParcelableCrashInfo 继承 CrashInfo,接下来我们看看 CrashInfo的实现。

CrashInfo

**CrashInfo ** 主要是将Crash信息文件名,类名,方法名,对应行号以及异常信息都封装到CrashInfo对象。

   // 描述 Crash 信息
    public static class CrashInfo {

        ...  ...
        public CrashInfo() {
        }

        //CrashInfo 初始化实例 
        public CrashInfo(Throwable tr) {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new FastPrintWriter(sw, false, 256);
            //输出栈trace
            tr.printStackTrace(pw);
            pw.flush();
            stackTrace = sanitizeString(sw.toString());
            exceptionMessage = tr.getMessage();

            // 显示异常的根本原因
            Throwable rootTr = tr;
            while (tr.getCause() != null) {
                tr = tr.getCause();
                if (tr.getStackTrace() != null && tr.getStackTrace().length > 0) {
                    rootTr = tr;
                }
                String msg = tr.getMessage();
                if (msg != null && msg.length() > 0) {
                    exceptionMessage = msg;
                }
            }
            // Crash 异常类名称 
            exceptionClassName = rootTr.getClass().getName();
            if (rootTr.getStackTrace().length > 0) {
                StackTraceElement trace = rootTr.getStackTrace()[0];
                // 获取 trace 文件名、类名、方法名、Crash 行号
                throwFileName = trace.getFileName();
                throwClassName = trace.getClassName();
                throwMethodName = trace.getMethodName();
                throwLineNumber = trace.getLineNumber();
            } else {
                throwFileName = "unknown";
                ... ... 
            }

            exceptionMessage = sanitizeString(exceptionMessage);
        }

三、handleApplicationCrash处理分析

handleApplicationCrash 会通过 JNI接口调用AMS中的方法。

               //发送 Crash 弹窗handler,直到Dialog  dismiss
                ActivityManager.getService().handleApplicationCrash(
                        mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));

ActivityManagerService.java
handleApplicationCrash 通过JNI 回调用 AMS中的handleApplicationCrash方法,进而调用AMS 中的内部方法handleApplicationCrashInner
handleApplicationCrash

  • 1.当远程IBinder对象为空Null时,则进程名为system_server

  • 2.当远程IBinder

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员Android

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值