当前位置: 首页 > article >正文

Android全局异常捕获

在开发过程中我们会使用try{}catch捕获一些异常,不过我们毕竟不能面面俱到,所以总会有一些异常在我们想不到的位置发生,然后程序就崩溃了,于是赶紧连上电脑复现bug,有的可以复现有的一时还复现不了,然后就各种手忙脚乱。

这时候我们就需要有一个全局异常捕获器,当有异常发生时,全局异常捕获器会输出异常信息,然后我们可以设置将异常信息保存到本地或者是上传服务器,方便我们快速的定位问题,不用为重新复现问题而搞的焦头烂额。

一、了解UncaughtExceptionHanlder

这里我们介绍使用UncaughtExceptionHandler来设置全局异常捕获器。首先我们来看看这个类。

源码:

@FunctionalInterface
public interface UncaughtExceptionHandler {
/**
* Method invoked when the given thread terminates due to the
* given uncaught exception.
* <p>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);
}
UncaughtExceptionHandler是java.Thread类中定义的一个接口。

作用:
用来处理在程序中未被捕获的异常。(如果程序中已经自己设置了try{}catch,则不会执行这个方法)。

二、实现方法

1.定义异常捕获类
新建MyCrashHandler 实现UncaughtExptionHandler接口:

public class MyCrashHandler implements Thread.UncaughtExceptionHandler {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        //在这里处理异常信息
    }
}

2.将得到的异常数据保存到本地(也可以上传服务器,这里根据需求自行解决)

/**
* 保存错误信息到文件中
* @param ex
*/
private void saveCrashInfoToFile(Throwable ex) {
    Writer writer = new StringWriter();
    PrintWriter printWriter = new PrintWriter(writer);
    ex.printStackTrace(printWriter);
    Throwable exCause = ex.getCause();
    while (exCause != null) {
        exCause.printStackTrace(printWriter);
        exCause =exCause.getCause();
    }
    printWriter.close();

    long timeMillis = System.currentTimeMillis();
    //错误日志文件名称
    String fileName = "crash-" + timeMillis + ".log";
    //判断sd卡可正常使用
    if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
      //文件存储位置
      String path = Environment.getExternalStorageDirectory().getPath() + "/crash_logInfo/";
      File fl = new File(path);
       //创建文件夹
        if(!fl.exists()) {
            fl.mkdirs();
        }
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(path + fileName);
            fileOutputStream.write(writer.toString().getBytes());
            fileOutputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

不要忘记配置读写权限:

<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 从SDCard读入数据权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

3.将该异常类设置为系统默认异常处理类,然后出现异常时,则该类会处理异常。

//设置该类为系统默认处理类
Thread.setDefaultUncaughtExceptionHandler(this);

4.在Application中使用:

MyCrashHandler mycrashHandler = new MyCrashHandler();
Thread.setDefaultUncaughtExceptionHandler(mycrashHandler);

第3步可以放到Application中,也可以在自身类里初始化好。这里只讲述思路。

到这里为止,就已经完成了全局捕获器的创建和调用,如果出现未捕获的异常,异常信息就会保存到sd卡内。这样就方便我们的查找。

当然上面的代码只是讲解思路,所以使用的时候,我们需要补充和完善,比如bug信息文件里添加手机信息,在保存到本地后将文件上传服务器等等操作,这些都可以根据需求自行完善。这里贴出我自己使用的一部分代码。

public class MyCrashHandler implements Thread.UncaughtExceptionHandler {

    private Thread.UncaughtExceptionHandler mDefaultHandler;
    private Context mcontext;
    private static MyCrashHandler myCrashHandler;

    private MyCrashHandler(){}

    public static synchronized MyCrashHandler newInstance() {
        if(myCrashHandler == null)
            myCrashHandler = new MyCrashHandler();
        return myCrashHandler;
    }

    /**
     * 初始化
     * @param context
     */
    public void init(Context context){
        mcontext = context;
        //系统默认处理类
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        //设置该类为系统默认处理类
        Thread.setDefaultUncaughtExceptionHandler(this);
    }



    @Override
    public void uncaughtException(Thread t, Throwable e) {
        if(!handleExample(e) && mDefaultHandler != null) { //判断异常是否已经被处理
            mDefaultHandler.uncaughtException(t, e);
        }else {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
            //退出程序
            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(1);
        }
    }

    /**
     * 提示用户出现异常
     * 将异常信息保存
     * @param ex
     * @return
     */
    private boolean handleExample(Throwable ex) {
        if(ex == null)
            return false;

        new Thread(() -> {
            Looper.prepare();
            Toast.makeText(mcontext, "很抱歉,程序出现异常,即将退出", Toast.LENGTH_SHORT).show();
            Looper.loop();
        }).start();

        //手机设备参数信息
        collectDeviceInfo(mcontext);
        saveCrashInfoToFile(ex);
        return true;
    }

    /**
     * 设备信息
     * @param mcontext
     */
    private void collectDeviceInfo(Context mcontext) {


    }


    /**
     * 保存错误信息到文件中
     * @param ex
     */
    private void saveCrashInfoToFile(Throwable ex) {
        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        ex.printStackTrace(printWriter);
        Throwable exCause = ex.getCause();
        while (exCause != null) {
            exCause.printStackTrace(printWriter);
            exCause = exCause.getCause();
        }
        printWriter.close();

        long timeMillis = System.currentTimeMillis();
        //错误日志文件名称
        String fileName = "crash-" + timeMillis + ".log";
        //判断sd卡可正常使用
        if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            //文件存储位置
            String path = Environment.getExternalStorageDirectory().getPath() + "/crash_logInfo/";
            File fl = new File(path);
            //创建文件夹
            if(!fl.exists()) {
                fl.mkdirs();
            }
            try {
                FileOutputStream fileOutputStream = new FileOutputStream(path + fileName);
                fileOutputStream.write(writer.toString().getBytes());
                fileOutputStream.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}


链接:https://www.jianshu.com/p/bafaea706eec


http://www.kler.cn/a/401962.html

相关文章:

  • 某校园网登录界面前端加密绕过
  • STM32单片机CAN总线汽车线路通断检测-分享
  • R语言 | 宽数据变成一列,保留对应的行名和列名
  • PH热榜 | 2024-11-19
  • 掌握SEO提升网站流量的关键在于长尾关键词的有效运用
  • 淘宝 NPM 镜像源
  • Android中常见内存泄漏的场景和解决方案
  • 代码辅助工具 GPT / Cursor
  • AWS EC2 ubuntu 使用密码登陆
  • 24.11.14 朴素贝叶斯分类 决策树-分类
  • 【算法】动态规划中01背包问题解析
  • uniapp 自定义加载组件,全屏加载,局部加载 (微信小程序)
  • git 基础之 merge 和 rebase 的比较
  • 运维面试题.云计算面试题之三ELK
  • VGG16-Pytorch实现人脸识别
  • C/C++实现tcp客户端和服务端的实现(从零开始写自己的高性能服务器)
  • AI 驱动低代码平台:开创智能化用户体验新纪元
  • vue功能基础元素使用
  • Java中日志采集框架-JUL、Slf4j、Log4j、Logstash
  • 在 macOS 和 Linux 中,波浪号 `~`的区别
  • 使用C++编写一个程序,模拟掷骰子的过程,输出1到6之间的随机数。
  • 【企业级分布式系统】ELK优化
  • 使用Go语言开发一个高性能的Web服务器,支持静态文件服务和实时通信。
  • 《深入理解 Spring MVC 工作流程》
  • 实验十三 生态安全评价
  • MySQL扩展varchar字段长度能否Online DDL