如何全面Catch你的Crash

针对于Android开发的小伙伴来说,对于Android Monitor的logcat再熟悉不过了,在这里我们可以查看到项目中的一些log信息,检测开发中出现的一些crash问题。不过由于种种原因,有的时候Android Monitor会闪屏或者crash信息丢失,比较不可控,不过倘若能将crash文件都存到手机上的我们自己创建的文件中岂不是更方便省事呢。

那么我们一起来研究如何创建一个CrashHander来解决

  • 1、第一步
    由于安全起见,我们需要catch所有类型的问题,那么也就需要实现Thread.UncaughtExceptionHandler接口,首先初始化 UncaughtExceptionHandler, 并设置该 CrashHander为程序的默认处理器
  public void init(Context context) {
        mContext = context;
        // 获取系统默认的UncaughtException处理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        // 设置该CrashHandler为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }
  • 2、 第二步
    作为程序员来说,我们在工作学习中的惯性思维是首先catch掉异常,所以接下来,我们重写uncaughtException方法来处理当UncaughtException发生时的异常情况(这里的mDefaultHandler是初始化时的Thread.UncaughtExceptionHandler)。
@Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if (!handleException(ex) && mDefaultHandler != null) {
            // 如果用户没有处理则让系统默认的异常处理器来处理
            mDefaultHandler.uncaughtException(thread, ex);
        } else {
            try {
                Thread.sleep(2500);
            } catch (InterruptedException e) {
                Logger.getLogger("error : " + e);
            }
            // 退出程序
            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(1);
        }
    }
  • 3、第三步
    之所以方便就是因为我们可以自定义写入crash的模式、crash的处理方式以及如何收集crash信息,发送crash报告(返回是否处理了该异常信息)。
private boolean handleException(Throwable ex) {
        if (ex == null) {
            return false;
        }
        ex.printStackTrace();
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                 //反馈给用户发生crash了
                Toast.makeText(mContext, R.string.text_error_crash, Toast.LENGTH_SHORT).show();
                Looper.loop();
            }
        }.start();
        //配置信息,如果在debug模式下
        if (Config.DEBUG) {
            // 收集设备参数信息
            collectDeviceInfo(mContext);
            // 保存日志文件
            saveCrashInfo2File(ex);
        }
        //线上的crash的话我们可以借助友盟,将crash上报友盟
        PrintsUMengUtil.reportError(mContext, ex);
        return true;
    }
  • 4、第四步
    收集设备参数信息,这里包括收集versionName和versionCode到file里
    public void collectDeviceInfo(Context ctx) {
        try {
            //这里的PackageManager,PackageInfo是Android API 24中提供的
            PackageManager pm = ctx.getPackageManager();
            PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
            if (pi != null) {
                String versionName = pi.versionName == null ? "null" : pi.versionName;
                String versionCode = pi.versionCode + "";
                infos.put("versionName", versionName);
                infos.put("versionCode", versionCode);
            }
        } catch (PackageManager.NameNotFoundException e) {
            Logger.getLogger(TAG, "an error occured when collect package info");
        }
        Field[] fields = Build.class.getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                infos.put(field.getName(), field.get(null).toString());
            } catch (Exception e) {
                Logger.getLogger(TAG, "an error occured when collect package info");
            }
        }
    }
  • 5、第五步
    保存错误信息到文件中,这里返回的是文件名称,便于将文件传送到服务器上。
private String saveCrashInfo2File(Throwable ex) {

        StringBuffer sb = new StringBuffer();
        for (Map.Entry<String, String> entry : infos.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            sb.append(key).append("=").append(value).append("\n");
        }

        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        ex.printStackTrace(printWriter);
        Throwable cause = ex.getCause();
        while (cause != null) {
            cause.printStackTrace(printWriter);
            cause = cause.getCause();
        }
        printWriter.close();
        String result = writer.toString();
        sb.append(result);
        try {
            long timestamp = System.currentTimeMillis();
            String time = formatter.format(new Date());
            String fileName = "crash-" + time + "-" + timestamp + ".log";
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                String path = "/sdcard/crash_news/";
                File dir = new File(path);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                FileOutputStream fos = new FileOutputStream(path + fileName);

                sb.append("\r\n报错日期:");
                sb.append(new Date(System.currentTimeMillis()).toLocaleString()).append("\r\n");
                printStackTrace(sb, ex);

                fos.write(sb.toString().getBytes());
                fos.close();
            }
            return fileName;
        } catch (Exception e) {
            Logger.getLogger(TAG, "an error occured while writing file...");
        }
        return null;
    }
    public void printStackTrace(StringBuffer sb, Throwable ex) {
        if (sb == null) sb = new StringBuffer();
        StackTraceElement[] trace = ex.getStackTrace();
        synchronized (sb) {
            sb.append(ex.toString() + "\r\n");
            for (int i = 0; i < trace.length; i++) {
                sb.append(" at " + trace[i] + "\r\n");
            }
            Throwable ourCause = ex.getCause();
            if (ourCause != null)
                printStackTraceAsCause(sb, ourCause, trace);
        }
    }
   private void printStackTraceAsCause(StringBuffer sb, Throwable ex, StackTraceElement[]
            causedTrace) {
        // assert Thread.holdsLock(s);
        // Compute number of frames in common between this and caused
        if (sb == null) sb = new StringBuffer();
        StackTraceElement[] trace = ex.getStackTrace();
        int m = trace.length - 1, n = causedTrace.length - 1;
        while (m >= 0 && n >= 0 && trace[m].equals(causedTrace[n])) {
            m--;
            n--;
        }
        int framesInCommon = trace.length - 1 - m;
        sb.append("Caused by: ");
        sb.append(ex + "\r\n");
        for (int i = 0; i <= m; i++) {
            sb.append(" at " + trace[i] + "\r\n");
        }
        if (framesInCommon != 0) {
            sb.append(" ..." + framesInCommon + " more \r\n");
        }

        // Recurse if we have a cause
        Throwable ourCause = ex.getCause();
        if (ourCause != null)
            printStackTraceAsCause(sb, ourCause, trace);
    }
  • 6、第6步
    网络请求数据时对onResponse函数中的,对于异常code的存储。
   public void saveLogInfo2File(String s) {

        if (TextUtils.isEmpty(s)) {
            return;
        }

        StringBuffer sb = new StringBuffer();
        for (Map.Entry<String, String> entry : infos.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            sb.append(key + "=" + value + "\n");
        }
        sb.append(s);
        try {
            long timestamp = System.currentTimeMillis();
            String time = formatter.format(new Date());
            String fileName = "log-" + time + "-" + timestamp + ".log";
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                String path = "/sdcard/crash_news/";
                File dir = new File(path);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                FileOutputStream fos = new FileOutputStream(path + fileName);

                sb.append("\r\nLog日期:");
                sb.append(new Date(System.currentTimeMillis()).toLocaleString() + "\r\n");

                fos.write(sb.toString().getBytes());
                fos.close();
            }
        } catch (Exception e) {
            Logger.getLogger(TAG, "an error occured while writing file..." + e);
        }
    }
  • 7、第七步
    对Crash进行初始化和调用。此处是在BaseApplication中通过initCrashHandler方法进行初始化。
 private void initCrashHandler() {
        CrashHandler crashHandler = CrashHandler.getInstance();
        crashHandler.init(getApplicationContext());
    }
  • 8、第八步
    在手机上下载并安装ES文件浏览器,由于这里自定义的crash文件的存储路径为/sdcard/crash_news/,文件名定义为crash_news了,当然了,这些都可以自行定义。


    crash_news.png

    打开crash文件就能看见捕获的crash文件,非常方便,like this:


    crash.jpg

    虽然我们比较不愿意看到crash ,但是面对crash我们还是不能放过任何一个,这样做如此一来便可以更加高效便捷的处理问题。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容

  • 一、Android Crash说明 程序因未捕获的异常而突然终止, 系统会调用处理程序的接口UncaughtExc...
    Mur阅读 3,033评论 0 6
  • 目录 认识与作用 Crash的捕获 Crash信息的获取 Crash日志写入上传 使用方式 一些注意 最后 认识与...
    justCode_阅读 1,446评论 1 0
  • /** * 1.全局捕获异常类 * 2.@authorDell * 3.@date2017/9/19 17:03 ...
    天天大保建阅读 211评论 0 0
  • public classCrashHandlerimplementsUncaughtExceptionHandle...
    红桃小花阅读 664评论 0 0
  • 关于作者米哈里·希斯赞特米哈伊,一直在关注人类的积极心理体验,他完成了大量经典的研究工作,提出并发展了心流理论,是...
    赵秀丽阅读 437评论 0 0