Day9 APP中抓取崩溃日志与邮件通知

点此进入目录:[干货] 十天 教你从创意到上线APP

一、异常的捕获

1、异常捕获类

首先,我们定义一个CrashHelper ,继承自UncaughtExceptionHandler ,这个类用来捕获系统出现的异常信息。

/**
 * Created by   : WGH.
 */
public class CrashHelper implements UncaughtExceptionHandler {

    private Context mContext;
    private static CrashHelper crashHelper;
    private UncaughtExceptionHandler mDefaultHandler;

    private CrashHelper() {
    }

    public static CrashHelper getInstance() {
        if (crashHelper == null) {
            crashHelper = new CrashHelper();
        }
        return crashHelper;
    }

    public void init(Context context) {
        mContext = context;
        // to get the default UncaughtException of system
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        // to set the CrashHandler as the default program handler
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    @Override
    public void uncaughtException(Thread thread, Throwable exception) {
        MessageUtil.postAppCrashMsg(exception, mDefaultHandler, thread);
    }
}

当系统出现异常的时候,我们就可以在这个uncaughtException()函数中捕获到异常和线程的一些相关信息,这里我们发送了异常的消息,然后就可以在Service中进行处理了。当然,在正常使用之前,还需要进行CrashHelper的初始化,我们将这部分工作放到MyApplication中进行。

2、全局MyApplication中的初始化

代码很简答,如下:

/**
 * Created by   : WGH.
 */
public class MyApplication extends Application{

    private MyApplication mMyApplication;

    @Override
    public void onCreate() {
        super.onCreate();
        mMyApplication = this;

        CrashHelper handler = CrashHelper.getInstance();
        handler.init(getApplicationContext());
    }

    public static MyApplication context() {
        return mMyApplication;
    }

    public static Gson getGson() {
        GsonBuilder builder = new GsonBuilder();
        return builder.create();
    }

    public static DaoMaster getDaoMaster() {
        if (daoMaster == null) {
            DaoMaster.OpenHelper helper = new DaoMaster.DevOpenHelper(mMyApplication, Define.DBNAME, null);
            daoMaster = new DaoMaster(helper.getReadableDatabase());
        }
        return daoMaster;
    }

    public static DaoSession getDaoSession() {
        if (daoSession == null) {
            if (daoMaster == null) {
                daoMaster = getDaoMaster();
            }
            daoSession = daoMaster.newSession();
        }
        return daoSession;
    }
}

可以看到,我们在MyApplication 中做了不少事情:getGson、getDaoSession等,这里初始化异常最主要的代码就是这两句:

        CrashHelper handler = CrashHelper.getInstance();
        handler.init(getApplicationContext());

二、异常的传递和处理

1、接收异常消息

在我们捕获到异常的时候,我们会在Service中进行消息的接收,然后对之进行处理(邮件上传)。

以下就是消息接收的代码:
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(CrashMessage message) {
        switch (message.what) {
            case PreDefine.AppCrash:
                mThread = message.thread;
                mException = message.throwable;
                mExceptionHandler = message.exceptionHandler;
                handleException(mException);
                break;
        }
    }

可以看到,我们使用了事件总线的方式来接收消息,然后在拿到各个参数后调用handleException(mException)进行后续的逻辑处理。

2、处理异常消息

    private boolean handleException(Throwable exception) {
        if (exception == null) {
            return false;
        }
        final StackTraceElement[] stack = exception.getStackTrace();
        final String message = exception.getMessage();
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                String fileName = Define.LogFileName;
                if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
                    File file = new File(Environment.getExternalStorageDirectory(), fileName);
                    mFilePath = file.getPath();
                    try {
                        FileOutputStream fileOutputStream = new FileOutputStream(file, true);
                        fileOutputStream.write((message + "\n\n").getBytes());
                        for (StackTraceElement stackTraceElement : stack) {
                            fileOutputStream.write(stackTraceElement.toString().getBytes());
                        }
                        fileOutputStream.flush();
                        fileOutputStream.close();
                        if (NetWorkUtil.isNetWork()) {
                            String[] toAddress = {PreDefine.getMailNumTo()};
                            EmailHelper.sendComplex(toAddress, MainActivity.activityMain.getResources()
                                    .getString(R.string.mail_title_crashlog), mEmailSubject, mFilePath);
                            deleteFile();
                        }
                        handleExceptionBySystem();
                    } catch (Exception e) {
                        DLog.e(e.toString());
                    }
                }
                Looper.loop();
            }
        }.start();
        return true;
    }

    private void handleExceptionBySystem() {
        mExceptionHandler.uncaughtException(mThread, mException);
    }

首先,我们通过exception.getStackTrace()获取到异常信息栈,然后通过exception.getMessage()获取异常信息,最后开启子线程将异常信息写入到文件流并发送出去。

关于邮件的逻辑我就不详细介绍了,感兴趣的可以参阅我的项目源码:GitHub

联系方式:

简书:WillFlow
GitHub:爱阅

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 123,377评论 18 134
  • 链表是线性表的一种。线性表是最基本、最简单、也是最常用的一种数据结构。 线性表中数据元素之间的关系是一对一的关系,...
    骑摩托马斯阅读 425评论 0 3
  • 2017,如期而至。我似乎从未如此期待一次新的年份,过去的所有都在新年带来那刻离我远去,或喜或悲,都已成过去。今天...
    郁笛雨洛阅读 113评论 0 0
  • 生成application子类,类中包含需要全局的变量 在配置文件ApplicationManifest.xml中...
    明慢慢阅读 2,602评论 4 11
  • 距离MOOC最近的一次:洪老师周六要去听关于怎么开MOOC的培训~
    雪杉非狐阅读 66评论 0 1