在开发过程中我们会使用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