Android 全局异常处理

  • 简介

        app崩溃,这个是大家都会遇到的问题,如果是我们自测,这个easy,我们很容易根据日志找到原因。但是有的时候并不会那么理想。 比如本人要和一个国内电视机厂商合作,因为应用都是内置到系统,并且一个人负责几个app。因为不在同一个城市,每次整机升级的时候都很痛苦。因为对方的测试也的确要测试很多东西。他们基本没时间帮你抓日志。直接把系统日志抛出来,基本都是那种20m以上的日志。这个时候就脑袋疼了。还有一点就是每次崩溃,都有一个提示框出来,也不美观。 让人觉得比较low。所以我们就需要自己处理app整体流程的崩溃。好了,吐槽完毕,上代码。

  • 代码分析

   1  首先是一个实现了UncaughtExceptionHandler接口的类,这个类来捕获app内部异常。

public class CrashCatchHandler implements Thread.UncaughtExceptionHandler {

    private static CrashCatchHandler INSTANCE;
    private Context mContext;
    private static final String PATH = Environment.getExternalStorageDirectory().getPath() + "/log/";

    private CrashCatchHandler() {

    }

    public static CrashCatchHandler getInstance() {
        if (null == INSTANCE) {
            synchronized (CrashCatchHandler.class) {
                if (null == INSTANCE) {
                    INSTANCE = new CrashCatchHandler();
                }
            }
        }
        return INSTANCE;
    }

    public void init(Context c) {
        //这里的c应该是全局的。
        if (c == null) {
            System.out.println("the context cann't be empty ");
            return;
        }
        mContext = c;
    }


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

        //如果是测试,那么只要写入本地日志即可,然后让测试拿到日志就可以。
        writeToFile(e);
        //在测试的时候无需上报
        uploadToServer();

        //杀死这个进程避免类似ios直接退出。
        Process.killProcess(Process.myPid());
    }

    /**
     * 把错误日志写到文件中。
     */
    private void writeToFile(Throwable ex) {
        //在这里需要确认具体的存储路径
        if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            return;
        }

        File dir = new File(PATH);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        File file = new File(PATH + time + ".txt");
        try {
            PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
            //导出发生异常的时间
            pw.println(time);
            pw.println();
            //导出异常的调用栈信息
            ex.printStackTrace(pw);

            pw.close();
        } catch (Exception e) {
        }
    }

    /**
     * 将错误日志上传到服务器
     */
    private void uploadToServer() {

    }
}

   其中最重要的是实现uncaughtException()这个函数,我们通过源码中可以看到,这个thread发生异常的时候会触发。

 /**
     * Interface for handlers invoked when a Thread abruptly
     * terminates due to an uncaught exception.
     * 

When a thread is about to terminate due to an uncaught exception * the Java Virtual Machine will query the thread for its * UncaughtExceptionHandler using * {@link #getUncaughtExceptionHandler} and will invoke the handler's * uncaughtException method, passing the thread and the * exception as arguments. * If a thread has not had its UncaughtExceptionHandler * explicitly set, then its ThreadGroup object acts as its * UncaughtExceptionHandler. If the ThreadGroup object * has no * special requirements for dealing with the exception, it can forward * the invocation to the {@linkplain #getDefaultUncaughtExceptionHandler * default uncaught exception handler}. * * @see #setDefaultUncaughtExceptionHandler * @see #setUncaughtExceptionHandler * @see ThreadGroup#uncaughtException * @since 1.5 */ @FunctionalInterface public interface UncaughtExceptionHandler { /** * Method invoked when the given thread terminates due to the * given uncaught exception. *

Any exception thrown by this method will be ignored by the * Java Virtual Machine. * @param t the thread * @param e the exception */ void uncaughtException(Thread t, Throwable e); }

2 实现了这个接口之后,需要注册一下。需要在application的子类中实现。

public class MyApp extends MultiDexApplication {

    @Override
    public void onCreate(){
        super.onCreate();
        CrashCatchHandler crashHandler = CrashCatchHandler.getInstance();
        crashHandler.init(this);
        Thread.setDefaultUncaughtExceptionHandler(crashHandler);
    }
}

 这样注册之后,就可以是实现捕获程序里面的错误日志了。

你可能感兴趣的:(Android)