【FastDev4Android框架开发】Android崩溃异常捕捉CustomCrash,提升用户体验(五)

转载请标明出处:
http://www.jianshu.com/p/70a32892d134
本文出自:【江清清博客-代号独狼】
(一):写在前面的话
接着上一篇继续更新,上一篇文章已经把FastDev4Android项目数据轻量级缓存ACache组件做了讲解和使用。今天项目更新客户端崩溃异常捕捉组件CustomCrash的讲解和使用。
本文章中的例子代码已经同步到FastDev4Android项目中,地址为:
https://github.com/jiangqqlmj/FastDev4Android
在平时我们都知道,Android系统的手机和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出现崩溃的现象,设备比较多,所以在程序发布出去之后,如果出现了崩溃现象,开发者应该及时获取在该设备上导致崩溃的信息,同时可以获取设备的相关信息和用户信息,这对于下一个版本的 BUG 修复帮助极大,所以今天就来介绍一下如何在程序崩溃的情况下收集相关的设备参数信息和具体的异常信息,并发送这些信息到服务器供开发者分析和调试程序。同时替换系统默认的崩溃弹框,提升应用的用户体验。
首先我们来看一下系统默认的崩溃弹框显示:

1.png

看上面的运行状态,一旦我们的应用出现了异常崩溃,立马会弹框,点击OK应用就退出了,这样用户也不知道发生了什么情况,一下子应用的用户体验下降了很多。更加严重的是,作为我们开发者还不知道云在用户手机的APP什么时候崩溃的,到底因为什么原因崩溃的。 OK下面我们来具体实现自定义的拦截崩溃异常的功能;
(二):具体实现
2.1:定义类 首先我们需要自定义一个实现Thead.UncaughtExceptionHandler的类,然后实现内部接口中定义的方法: void uncaughtException(Thread thread, Throwable ex); 具体如下:

public class CustomCrash  implements Thread.UncaughtExceptionHandler
/*
     * (non-Javadoc) 进行重写捕捉异常
     *
     * @see
     * java.lang.Thread.UncaughtExceptionHandler#uncaughtException(java.lang
     * .Thread, java.lang.Throwable)
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if(type_save==TYPE_SAVE_SDCARD){
            // 1,保存信息到sdcard中
            saveToSdcard(mContext, ex);
        }else if(type_save==TYPE_SAVE_REMOTE){
            // 2,异常崩溃信息投递到服务器
            saveToServer(mContext,ex);
        }
        // 3,应用准备退出
        showToast(mContext, "很抱歉,程序发生异常,即将推出.");
        try {
            Thread.sleep(3500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        ManagerActivity.getInstance().finishActivity();
        android.os.Process.killProcess(android.os.Process.myPid());

    }

2.2.上下文注册 我们需要把当前应用的上下文注册到系统的异常处理器中。这样就让系统执行我们自定义的异常捕捉器。

public void setCustomCrashInfo(Context pContext) {
        this.mContext = pContext;
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

2.3:崩溃异常日志保存
①:数据保存到SDCard中,直接转换异常日志信息,写入SDCard文件中如下:

/**
     * 保存异常信息到sdcard中
     *
     * @param pContext
     * @param ex
     *            异常信息对象
     */
    private void saveToSdcard(Context pContext, Throwable ex) {
        String fileName = null;
        StringBuffer sBuffer = new StringBuffer();
        // 添加异常信息
        sBuffer.append(getExceptionInfo(ex));
        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) {
            File file1 = new File(CRASH_SAVE_SDPATH);
            if (!file1.exists()) {
                file1.mkdir();
            }
            fileName = file1.toString() + File.separator + paserTime(System.currentTimeMillis()) + ".log";
            File file2 = new File(fileName);
            FileOutputStream fos;
            try {
                fos = new FileOutputStream(file2);
                fos.write(sBuffer.toString().getBytes());
                fos.flush();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

②:数据POST投递到服务器中,进行接口存入相应的文件或者数据库中

 /**
     * 进行把数据投递至服务器
     * @param pContext
     * @param ex  崩溃异常
     */
    private void saveToServer(Context pContext,Throwable ex){
        final String carsh_log=getExceptionInfo(ex);
        new Thread(new Runnable() {
            @Override
            public void run() {
                HashMap<String,String> params=new HashMap<String,String>();
                params.put("crash_log",carsh_log);
                String result= IoUtils.responseFromServiceByGetNo(CARSH_LOG_DELIVER, params);
                if (result.equals("1")){
                    Log.d(TAG,"崩溃日志投递成功...");
                }else {
                    Log.d(TAG,"崩溃日志投递失败...");
                }
            }
        }).start();
    }

2.4:异常捕捉器具体使用: 我们需要在自定义的Application初始化方法进行初始化我们的捕捉器,然后设置改变系统捕捉器处理方式:

@Override
    public void onCreate() {
        super.onCreate();
        this.instance=this;
        //初始化崩溃日志收集器
        CustomCrash mCustomCrash=CustomCrash.getInstance();
        mCustomCrash.setCustomCrashInfo(this);

    }

OK下面我们来具体来使用一下,我们故意制造一个空指针异常:结果分别如下:


2.png

3.png

4.png

5.png

6.png

2.5:由于上面对于类的核心方法进行了讲解,并且该类其他也没有多少行代码,方便大家阅读这边直接把整个类复制如下,并且为了大家测试使用,类中崩溃日志投递的地址也是可以正常使用的。

package com.chinaztt.fda.crash;

import android.content.Context;
import android.os.Environment;
import android.os.Looper;
import android.widget.Toast;

import com.chinaztt.fda.utils.IoUtils;
import com.chinaztt.fda.utils.Log;
import com.chinaztt.fda.utils.ManagerActivity;
import com.chinaztt.fda.utils.StrUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.TimeZone;

/**
 * 当前类注释:客户端运行 异常崩溃数据扑捉异常保存SD卡或者实时投递服务器工具类
 * 项目名:FastDev4Android
 * 包名:com.chinaztt.fda.crash
 * 作者:江清清 on 15/10/26 13:29
 * 邮箱:jiangqqlmj@163.com
 * QQ: 781931404
 * 公司:江苏中天科技软件技术有限公司
 */
public class CustomCrash  implements Thread.UncaughtExceptionHandler{
    private static final String TAG="CustomCrash";
    private static final int TYPE_SAVE_SDCARD=1; //崩溃日志保存本地SDCard  --建议开发模式使用
    private static final int TYPE_SAVE_REMOTE=2; //崩溃日志保存远端服务器 --建议生产模式使用

    private int type_save=2;  //崩溃保存日志模式 默认为2,采用保存Web服务器
    private static final String CRASH_SAVE_SDPATH="sdcard/fda_cache/"; //崩溃日志SD卡保存路径
    private static final String CARSH_LOG_DELIVER="http://img2.xxh.cc:8080/SalesWebTest/CrashDeliver";
    private static CustomCrash instance = new CustomCrash();
    private Context mContext;
    private CustomCrash() {
    }
    /**
     *
     * @return
     */
    public static CustomCrash getInstance() {
        return instance;
    }

    /*
     * (non-Javadoc) 进行重写捕捉异常
     *
     * @see
     * java.lang.Thread.UncaughtExceptionHandler#uncaughtException(java.lang
     * .Thread, java.lang.Throwable)
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if(type_save==TYPE_SAVE_SDCARD){
            // 1,保存信息到sdcard中
            saveToSdcard(mContext, ex);
        }else if(type_save==TYPE_SAVE_REMOTE){
            // 2,异常崩溃信息投递到服务器
            saveToServer(mContext,ex);
        }
        // 3,应用准备退出
        showToast(mContext, "很抱歉,程序发生异常,即将推出.");
        try {
            Thread.sleep(3500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        ManagerActivity.getInstance().finishActivity();
        android.os.Process.killProcess(android.os.Process.myPid());

    }

    /**
     * 设置自定异常处理类
     *
     * @param pContext
     */
    public void setCustomCrashInfo(Context pContext) {
        this.mContext = pContext;
        Thread.setDefaultUncaughtExceptionHandler(this);
    }
    /**
     * 保存异常信息到sdcard中
     *
     * @param pContext
     * @param ex
     *            异常信息对象
     */
    private void saveToSdcard(Context pContext, Throwable ex) {
        String fileName = null;
        StringBuffer sBuffer = new StringBuffer();
        // 添加异常信息
        sBuffer.append(getExceptionInfo(ex));
        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) {
            File file1 = new File(CRASH_SAVE_SDPATH);
            if (!file1.exists()) {
                file1.mkdir();
            }
            fileName = file1.toString() + File.separator + paserTime(System.currentTimeMillis()) + ".log";
            File file2 = new File(fileName);
            FileOutputStream fos;
            try {
                fos = new FileOutputStream(file2);
                fos.write(sBuffer.toString().getBytes());
                fos.flush();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 进行把数据投递至服务器
     * @param pContext
     * @param ex  崩溃异常
     */
    private void saveToServer(Context pContext,Throwable ex){
        final String carsh_log=getExceptionInfo(ex);
        new Thread(new Runnable() {
            @Override
            public void run() {
                HashMap<String,String> params=new HashMap<String,String>();
                params.put("crash_log",carsh_log);
                String result= IoUtils.responseFromServiceByGetNo(CARSH_LOG_DELIVER, params);
                if (result.equals("1")){
                    Log.d(TAG,"崩溃日志投递成功...");
                }else {
                    Log.d(TAG,"崩溃日志投递失败...");
                }
            }
        }).start();
    }
    /**
     * 获取并且转化异常信息
     * 同时可以进行投递相关的设备,用户信息
     * @param ex
     * @return 异常信息的字符串形式
     */
    private String getExceptionInfo(Throwable ex) {
        StringWriter sw = new StringWriter();
        ex.printStackTrace(new PrintWriter(sw));
        StringBuffer stringBuffer=new StringBuffer();
        stringBuffer.append("---------Crash Log Begin---------\n");
        //在这边可以进行相关设备信息投递--这边就稍微设置几个吧
        //其他设备和用户信息大家可以自己去扩展收集上传投递
        stringBuffer.append("SystemVersion:"+ StrUtils.getLocalSystemVersion()+"\n");
        stringBuffer.append(sw.toString()+"\n");
        stringBuffer.append("---------Crash Log End---------\n");
        return stringBuffer.toString();
    }
    /**
     * 进行弹出框提示
     *
     * @param pContext
     * @param msg
     */
    private void showToast(final Context pContext, final String msg) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                Toast.makeText(pContext, msg, Toast.LENGTH_SHORT).show();
                Looper.loop();
            }
        }).start();
    }
    /**
     * 将毫秒数转换成yyyy-MM-dd-HH-mm-ss的格式
     * @param milliseconds
     * @return
     */
    private String paserTime(long milliseconds) {
        System.setProperty("user.timezone", "Asia/Shanghai");
        TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
        TimeZone.setDefault(tz);
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
        String times = format.format(new Date(milliseconds));

        return times;
    }
}

到此为止我们今天自定义异常捕捉CustomCrash的讲解和使用结果,
详细代码项目地址: https://github.com/jiangqqlmj/FastDev4Android
同时欢迎大家star和fork整个开源快速开发框架项目~如果有什么意见和反馈,欢迎留言,必定第一时间回复。也欢迎有同样兴趣的童鞋加入到该项目中来,一起维护该项目。

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,376评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • —小荻老师在选择自己大本营分享整理 1 本次分享的核心要点是: 2 我们遇到的80%以上的问题基本上是沟通问题,我...
    心声树洞阅读 1,101评论 0 1
  • 前段时间看文章的时候,有看到作者推荐了《非暴力沟通》这本书,后来又被其他写作者推荐过,心想应该是本不错的书,加之我...
    希亚阅读 318评论 0 0
  • 当窗前不再映着皎洁的明月。当岁月悄然静止。。。许。。。浮世奢华也只是一际云烟。若年华倒数。我仰望墨色苍穹,亿万星辰...
    程子寒阅读 495评论 6 4