Skip to content

Android(7): Crash及ANR捕捉上报

clarkehe edited this page Aug 10, 2016 · 2 revisions

Crash的捕捉有助于监控程序Crash率,同时通过捕捉Crash的堆栈信息来定位问题并修复。 Android中捕捉异常信息,通过系统提示的接口就可以了。代码大概如下:

public class CatchCrash {
    private final   static String TAG = "CatchCrash";
    private static  Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler;

    public static void catchUnhandledException(){
        defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(new CustomUncaughtExceptionHandler());
    }

    private static class CustomUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
        @Override
        public void uncaughtException(Thread thread, Throwable ex) {
            Log.e(TAG, "***-------------");
            ex.printStackTrace();
            Log.e(TAG, "uncaughtException, thread:" + thread.getName() + ", ex:" + ex.toString());
            Log.e(TAG, "***-------------");
            defaultUncaughtExceptionHandler.uncaughtException(thread, ex);
        }
    }
}

我们写一段代码,故意制造Crash, 就可以看到打印的日志信息,在实际项目,需要这些信息和其他的一些信息一起打包上报到Crash平台。

    findViewById(R.id.makeCrashBut).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    String test = null;
                    Log.d("test", "" + test.length());
                }
            }).start();
        }
08-10 20:34:53.758 15317-15457/com.sample E/CatchCrash: ***-------------
08-10 20:34:53.759 15317-15457/com.sample W/System.err: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference
08-10 20:34:53.760 15317-15457/com.sample W/System.err:     at com.sample.crashreport.TestCrashActivity$2$1.run(TestCrashActivity.java:32)
08-10 20:34:53.760 15317-15457/com.sample W/System.err:     at java.lang.Thread.run(Thread.java:818)
08-10 20:34:53.761 15317-15457/com.sample E/CatchCrash: uncaughtException, thread:Thread-11103, ex:java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference
08-10 20:34:53.761 15317-15457/com.sample E/CatchCrash: ***-------------

根据Java异常的处理逻辑,代码没有catch处理的异常,最后都会由线程的默认UncaughtExceptionHandler进行处理。如果我们用try_catch将上面产生的异常的代码包住,则程序就不会输出上面的异常信息了。

这个默认的UncaughtExceptionHandler是一个静态变量,应该是进程初始化时设置的,所有线程共享。另外看代码,每个线程还有一个私有的UncaughtExceptionHandler,默认值是null,需要自己进行设置。

    /**
     * Holds the handler for uncaught exceptions in this Thread,
     * in case there is one.
     */
    private UncaughtExceptionHandler uncaughtHandler;

    /**
     * Holds the default handler for uncaught exceptions, in case there is one.
     */
    private static UncaughtExceptionHandler defaultUncaughtHandler;

如果设置了私有的UncaughtExceptionHandler,静态UncaughtExceptionHandler则不会生效。

经过调试发现,线程默认的异常处理是由一个叫class com.android.internal.os.RuntimeInit$UncaughtHandler【源码】的类实现的。它的处理逻辑是首先输出相关的异常信息,弹出一个对话框让用户确认,对话框消失后,结束进程。

    private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
        public void uncaughtException(Thread t, Throwable e) {
            try {
                // Don't re-enter -- avoid infinite loops if crash-reporting crashes.
                if (mCrashing) return;
                mCrashing = true;

                if (mApplicationObject == null) {
                    Slog.e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
                } else {
                    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());
                    Slog.e(TAG, message.toString(), e);
                }

                // Bring up crash dialog, wait for it to be dismissed
                ActivityManagerNative.getDefault().handleApplicationCrash(
                        mApplicationObject, new ApplicationErrorReport.CrashInfo(e));
            } catch (Throwable t2) {
                try {
                    Slog.e(TAG, "Error reporting crash", t2);
                } catch (Throwable t3) {
                    // Even Slog.e() fails!  Oh well.
                }
            } finally {
                // Try everything to make sure this process goes away.
                Process.killProcess(Process.myPid());
                System.exit(10);
            }
        }
    }

ANR是反应系统流畅度的,ANR的发生一定程度影响了用户的体验。ANR也是需要监控的指标。

https://github.com/SalomonBrys/ANR-WatchDog

Clone this wiki locally