Android进阶——Crash异常捕获并发送到服务器

前言

在项目中,我们常常会遇到Crash的现象,也就是程序崩溃的时候,这个时候最常看到的就是这个界面

如果你的项目已经发布到市场上了,这样的崩溃对于开发人员是看不到的,所以我们得想方法将崩溃信息发送到服务器,交给我们的程序员查看,Google考虑到这一点,也提供了Thread.UncaughtExceptionHandler接口来实现这一问题

Crash异常捕获的简单使用

创建Crash异常捕获很简单,主要的步骤有:

  1. 创建BaseApplication继承Application并实现Thread.UncaughtExceptionHandler
  2. 通过Thread.setDefaultUncaughtExceptionHandler(this)设置默认的异常捕获
  3. 最后在manifests中注册创建的BaseApplication
public class BaseApplication extends Application implements Thread.UncaughtExceptionHandler {

    @Override
    public void onCreate() {
        super.onCreate();
        //设置异常捕获
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    @Override
    public void uncaughtException(final Thread thread, final Throwable ex) {
        //当有异常产生时执行该方法
    }
}

我们可以在uncaughtException()方法中输出异常信息,并让它隔两秒杀死自己进程,这样就不会弹出崩溃的弹窗,让它直接退出程序

/**
 * 当有异常产生时执行该方法
 * @param thread 当前线程
 * @param ex 异常信息
 */
@Override
public void uncaughtException(final Thread thread, final Throwable ex) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            Log.e("TAG","currentThread:"+Thread.currentThread()+"---thread:"+thread.getId()+"---ex:"+ex.toString());
        }
    }).start();
    SystemClock.sleep(2000);
    android.os.Process.killProcess(android.os.Process.myPid());
}

最后一步,别忘了在manifests中注册BaseApplication

<application
    android:name=".Base.BaseApplication"
    android:allowBackup="true"
    android:icon="@mipmap/ic_logo"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

我们通过运行这个方法,来测试我们的程序

private void testError() {
    int a = 10 / 0;
}

查看Log信息,验证我们的错误信息

12-18 11:40:02.074 7510-7555/com.handsome.ap E/TAG: currentThread:Thread[Thread-325,5,main]---thread:1---ex:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.handsome.ap/com.handsome.ap.Activity.MainActivity}: 
java.lang.ArithmeticException: divide by zero

完整代码

public class BaseApplication extends Application implements Thread.UncaughtExceptionHandler {

    @Override
    public void onCreate() {
        super.onCreate();
        //设置异常捕获
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
     * 当有异常产生时执行该方法
     * @param thread 当前线程
     * @param ex 异常信息
     */
    @Override
    public void uncaughtException(final Thread thread, final Throwable ex) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.e("TAG","currentThread:"+Thread.currentThread()+"---thread:"+thread.getId()+"---ex:"+ex.toString());
            }
        }).start();
        SystemClock.sleep(2000);
        android.os.Process.killProcess(android.os.Process.myPid());
    }
}

Crash异常捕获并发送到服务器

其实这里就是将上面的简单使用进行封装,在一个类中处理相关的逻辑,主要步骤和上面是一样的

public class CrashHandler implements Thread.UncaughtExceptionHandler {

    /**
     * 捕获异常回掉
     *
     * @param thread 当前线程
     * @param ex     异常信息
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        //导出异常信息到SD卡
        dumpExceptionToSDCard(ex);
        //上传异常信息到服务器
        uploadExceptionToServer(ex);
        //延时1秒杀死进程
        SystemClock.sleep(2000);
        Process.killProcess(Process.myPid());
    }
}

我们为下面的信息保存先提供一些成员变量

//文件夹目录
private static final String PATH = Environment.getExternalStorageDirectory().getPath() + "/crash_log/";
//文件名
private static final String FILE_NAME = "crash";
//文件名后缀
private static final String FILE_NAME_SUFFIX = ".trace";
//上下文
private Context mContext;

//单例模式
private static CrashHandler sInstance = new CrashHandler();
private CrashHandler() {}
public static CrashHandler getInstance() {
    return sInstance;
}

提供一个初始化的方法,记得调用Thread.setDefaultUncaughtExceptionHandler(this)这个方法

/**
 * 初始化方法
 *
 * @param context
 */
public void init(Context context) {
    //将当前实例设为系统默认的异常处理器
    Thread.setDefaultUncaughtExceptionHandler(this);
    //获取Context,方便内部使用
    mContext = context.getApplicationContext();
}

剩下的就是保存异常信息了,这里发送到服务端采用的是Bmob第三方后端云

/**
 * 导出异常信息到SD卡
 *
 * @param ex
 */
private void dumpExceptionToSDCard(Throwable ex) {
    if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
        return;
    }
    //创建文件夹
    File dir = new File(PATH);
    if (!dir.exists()) {
        dir.mkdirs();
    }
    //获取当前时间
    long current = System.currentTimeMillis();
    String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(current));
    //以当前时间创建log文件
    File file = new File(PATH + FILE_NAME + time + FILE_NAME_SUFFIX);
    try {
        //输出流操作
        PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
        //导出手机信息和异常信息
        PackageManager pm = mContext.getPackageManager();
        PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
        pw.println("发生异常时间:" + time);
        pw.println("应用版本:" + pi.versionName);
        pw.println("应用版本号:" + pi.versionCode);
        pw.println("android版本号:" + Build.VERSION.RELEASE);
        pw.println("android版本号API:" + Build.VERSION.SDK_INT);
        pw.println("手机制造商:" + Build.MANUFACTURER);
        pw.println("手机型号:" + Build.MODEL);
        ex.printStackTrace(pw);
        //关闭输出流
        pw.close();
    } catch (Exception e) {

    }
}

/**
 * 上传异常信息到服务器
 *
 * @param ex
 */
private void uploadExceptionToServer(Throwable ex) {
    Error error = new Error(ex.getMessage());
    error.save(new SaveListener<String>() {
        @Override
        public void done(String objectId, BmobException e) {

        }
    });
}

在我们的Application中创建该异常捕获

CrashHandler crashHandler = CrashHandler.getInstance();
crashHandler.init(this);

我们同样按照上面的方法来测试这个异常捕获,运行程序,在文件夹中找到我们创建的目录

找到对应文件

查看对应信息

后台数据库的信息

完整代码

public class CrashHandler implements Thread.UncaughtExceptionHandler {

    //文件夹目录
    private static final String PATH = Environment.getExternalStorageDirectory().getPath() + "/crash_log/";
    //文件名
    private static final String FILE_NAME = "crash";
    //文件名后缀
    private static final String FILE_NAME_SUFFIX = ".trace";
    //上下文
    private Context mContext;

    //单例模式
    private static CrashHandler sInstance = new CrashHandler();
    private CrashHandler() {}
    public static CrashHandler getInstance() {
        return sInstance;
    }

    /**
     * 初始化方法
     *
     * @param context
     */
    public void init(Context context) {
        //将当前实例设为系统默认的异常处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
        //获取Context,方便内部使用
        mContext = context.getApplicationContext();
    }

    /**
     * 捕获异常回掉
     *
     * @param thread 当前线程
     * @param ex     异常信息
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        //导出异常信息到SD卡
        dumpExceptionToSDCard(ex);
        //上传异常信息到服务器
        uploadExceptionToServer(ex);
        //延时1秒杀死进程
        SystemClock.sleep(2000);
        Process.killProcess(Process.myPid());
    }

    /**
     * 导出异常信息到SD卡
     *
     * @param ex
     */
    private void dumpExceptionToSDCard(Throwable ex) {
        if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            return;
        }
        //创建文件夹
        File dir = new File(PATH);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        //获取当前时间
        long current = System.currentTimeMillis();
        String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(current));
        //以当前时间创建log文件
        File file = new File(PATH + FILE_NAME + time + FILE_NAME_SUFFIX);
        try {
            //输出流操作
            PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
            //导出手机信息和异常信息
            PackageManager pm = mContext.getPackageManager();
            PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
            pw.println("发生异常时间:" + time);
            pw.println("应用版本:" + pi.versionName);
            pw.println("应用版本号:" + pi.versionCode);
            pw.println("android版本号:" + Build.VERSION.RELEASE);
            pw.println("android版本号API:" + Build.VERSION.SDK_INT);
            pw.println("手机制造商:" + Build.MANUFACTURER);
            pw.println("手机型号:" + Build.MODEL);
            ex.printStackTrace(pw);
            //关闭输出流
            pw.close();
        } catch (Exception e) {

        }
    }

    /**
     * 上传异常信息到服务器
     *
     * @param ex
     */
    private void uploadExceptionToServer(Throwable ex) {
        Error error = new Error(ex.getMessage());
        error.save(new SaveListener<String>() {
            @Override
            public void done(String objectId, BmobException e) {

            }
        });
    }
}

CrashHandler源码下载

  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

许英俊潇洒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值