Java / Android 监控进程Crash异常

在Android/Java开发中经常遇到的就是程序突然崩溃了,这中崩溃是如何被捕获并且打印到控制面板的呢。抛出异常之后app就崩溃了,而不是整个系统崩溃是为什么?今天就从Android的角度来看看一个Android系统是如何崩溃的。

Java / Android 监控进程Crash异常_第1张图片

1.异常监控的注册

从Android系统启动流程知道,systemserver和每个app进程的启动都是通过Zygote进程孵化的。从Launcher桌面点击启动一个未启动过的app时,就会触发zygote孵化一个新的进程,从这里开始扯出系统对crash的处理。对于进程的创建和android系统的启动不熟悉的可以自行了解。

 public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
        if (RuntimeInit.DEBUG) {
            Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
        }
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
        RuntimeInit.redirectLogStreams();
        RuntimeInit.commonInit();// 1 
        ZygoteInit.nativeZygoteInit(); // 2
        return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);//3
    }

一个app进程被fork出来之后,就会走到这个方法去初始化当前进程运行需要的条件环境,注释1初始化日志、crash监控等。注释2初始化binder线程池,表明当前进程就可以使用binder来通信。注释3利用反射调用 ActivityThread 的main()方法,这里是我们熟悉的主线程。这里主要关注注释1处。其他功能不在本次讨论范围。

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.
         */
        Thread.setUncaughtExceptionPreHandler(new LoggingHandler());//1
        Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler());//2
}

注释1主要是打印log 崩溃信息,但是和注释2是互斥的,如果注释2走了就不会走注释1的方法,因为有个boolean标识符做限制,这里不过多解释,主要看注释2的处理,因为这里涉及到了 kill application 的操作。都是传给Thread进行处理的,接下来会去看Thread的实现。

   private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
        public void uncaughtException(Thread t, Throwable e) {
            try {
                //防止重复打印崩溃日志,这就是上面说的和LoggingHandler互斥
                if (mCrashing) return;
                mCrashing = true;
                // 弹出崩溃dialog,触发AMS处理崩溃信息
                ActivityManager.getService().handleApplicationCrash(
                        mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
            } finally {
                // 杀死当前进程
                Process.killProcess(Process.myPid());
                System.exit(10);
            }
        }
    }
KillApplicationHandler等都是实现Thread中的 Thread.UncaughtExceptionHandler 接口,接下来转到Thread中
private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;

 public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
         defaultUncaughtExceptionHandler = eh;
    }
public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler(){
        return defaultUncaughtExceptionHandler;
    }

Thread中处理很简单只是把他设置成全局静态变量。为什么是static的呢?也就是说这个崩溃并不是当前 Thread 的,而是Thread类的属性。跟当前对象没关系。不管是那一个线程崩溃都会触发,当然Thread中也保存了当前线程独有的UncaughtExceptionHandler对象,只是这个对象一般为空。因为对于android来说一个app代表一个进程,每个app进程都有独立的JVM虚拟机,所以进程中的所有线程都是运行在当前app的虚拟机内的。不管是任何线程触发了崩溃的信息,都会导致整个JVM的崩溃,所以每当崩溃时我们的app程序就退出运行。

到这里set之后我们的当前进程已经把callback注册到了Thread类中,那这个callback何时会被调用呢?

2.触发异常抛出

在上文了解到了,异常监控接口注册到了Thread类中,那产生异常时候,在哪里被出发的呢?

既然崩溃是通过监听线程类触发,不论是那个线程只要出现异常就会触发崩溃。我们知道一个进程对应一个虚拟机,对于一个进程中的所有线程,也通过JVM虚拟机来管理的。所以当出现异常信息时,jvm 会去搜是那个线程出了问题,然后调用线程类的

public final void dispatchUncaughtException(Throwable e) {
        Thread.UncaughtExceptionHandler initialUeh =
                Thread.getUncaughtExceptionPreHandler(); // 1
        if (initialUeh != null) {
            try {
                initialUeh.uncaughtException(this, e);
            } catch (RuntimeException | Error ignored) {
                // Throwables thrown by the initial handler are ignored
            }
        }
        // 2
        getUncaughtExceptionHandler().uncaughtException(this, e);
    }

当程序出现异常时JVM虚拟机就会调用 dispatchUncaughtException()方法,注释1就会回调 LoggingHandler 的方法uncaughtException()。主要看注释2

   public UncaughtExceptionHandler getUncaughtExceptionHandler() {
        return uncaughtExceptionHandler != null ?
            uncaughtExceptionHandler : group;
    }

上述方法中 uncaughtExceptionHandler 是线程独立的异常信息监控,一般为空。所以注释2中获取到的都是ThreadGroup,ThreadGroup也是 Thread.UncaughtExceptionHandler 类型。在ThreadGroup中实现

    public void uncaughtException(Thread t, Throwable e) {
        if (parent != null) {
            parent.uncaughtException(t, e);
        } else {
            Thread.UncaughtExceptionHandler ueh =
                Thread.getDefaultUncaughtExceptionHandler(); // 1
            ueh.uncaughtException(t, e);
        }
    }

ThreadGroup 是通过获取父线程的ThreadGroup对象。注释1 获取到的对象其实就是KillApplicationHandler对象

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.
         */
        Thread.setUncaughtExceptionPreHandler(new LoggingHandler());//1
        Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler());//2
}

所以在ThreadGroup中回调就会调到

ActivityManager.getService().handleApplicationCrash(
        mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));

然后转到AMS中去。

    void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
            ApplicationErrorReport.CrashInfo crashInfo) {
        // 1
        addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
        // 2
        mAppErrors.crashApplication(r, crashInfo);
    }

注释1中通过跨进程把日志打印到具体的路径下面。注释2 主要是提示错误信息,然后弹出 crash dialog。主要看注释 1代码.

public void addErrorToDropBox(String eventType,
            ProcessRecord process, String processName, ActivityRecord activity,
            ActivityRecord parent, String subject,
            final String report, final File dataFile,
            final ApplicationErrorReport.CrashInfo crashInfo) {
     
        final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class); // 1

        if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return;

                dbox.addText(dropboxTag, sb.toString());
            }
        };
    }

注释1获取系统服务,这个服务是在SystemServer中启动的,主要就是保持系统和application的崩溃日志,然后保持到 (“data/system/dropbox/”)下面。
这个服务在SystemServer中启动

mSystemServiceManager.startService(DropBoxManagerService.class);

调用addText() 方法通过流保存到文件夹下。

 

你可能感兴趣的:(Android,framework源码)