当用户在使用app出现崩溃现象时,我们需要知道是什么原因,并将原因记录下来上到服务器,这样以后我们就可以知道具体是什么原因了
一般而言,我们在开发的时候为了避免出现bug,在一些疑似有可能出现bug的地方会主动加上try{ } catch( ) { },当然有些时候编译器的检测机制也会提示我们需要加try catch比如JSONObject取值的时候就会让做空指针异常处理。
不过不管做多少预处理,但是!!!bug总会在你意想不到的时候突然降临-。-
没有一点点防备,也没有一丝顾虑,你就这样出现在我的世界里,带给我惊喜,情不自已
如果是在开发阶段的话还不是一个多大的问题,插上USB调试看下log就行虽然并不一定能够稳定复现,
but!!!项目上线出了bug,先不说用户体验不好,单开发者自己而言也是很悲催的一件事。单凭热心用户
所以,这时候我们就需要一种方法,让我们知道用户app崩溃的原因。
这也就是这篇文章罗里吧嗦这么久要讲的 囧oz
和以前一样,先贴代码
public class CrashHandler implements UncaughtExceptionHandler {
private static CrashHandler instance = null;
private CrashHandlerListener crashHandlerListener = null;
private UncaughtExceptionHandler defaultUncaughtExceptionHandler = null;
private CrashHandler(){ }
public static CrashHandler getInstance() {
if (instance == null)
instance = new CrashHandler();
return instance;
}
public void init() {
// 获取系统默认的为捕获异常处理器
this.defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
// 设置当线程由于未捕获到异常而突然终止,并且没有为该线程定义其他处理程序时所调用的默认处理程序。
Thread.setDefaultUncaughtExceptionHandler(this);
}
public void init(CrashHandlerListener listener) {
init();
this.crashHandlerListener = listener;
}
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
writeExceptionToSDCard(throwable);
uploadedExceptionToServer(throwable);
// 如果系统存在默认的未捕获异常处理器则让系统处理,否则自己处理
if (defaultUncaughtExceptionHandler != null) {
defaultUncaughtExceptionHandler.uncaughtException(thread, throwable);
} else {
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
if (crashHandlerListener != null) {
crashHandlerListener.uncaughtException();
} else {
ToastUtil.showShort("很抱歉,程序出现异常,即将退出.");
new Handler().postDelayed(new Runnable(){
public void run() {
App.getInstance().exit();
}
}, 2000);
}
Looper.loop();
}
}).start();
}
}
/**
* 将异常信息写入SD卡
* @param throwable 未捕获异常
*/
private void writeExceptionToSDCard(Throwable throwable) {
// TODO: 2017/8/4 将异常信息写入SD卡
Log.i("###########", throwable.toString());
}
/**
* 将异常信息上传到服务器
* @param throwable 未捕获异常
*/
private void uploadedExceptionToServer(Throwable throwable) {
// TODO: 2017/8/4 将异常信息上传到服务器
}
public interface CrashHandlerListener {
void uncaughtException();
}
}
单例
其实没有必要使用单例模式,我这边使用单例仅是强迫症。。。因为CrashHandler是需要在整个App运行状态中去捕获全局所有异常的,因此一般都是在Application中去初始化,然后初始化之后也没有其他什么操作了,毕竟目的是捕获异常。所以总体而言,CrashHandler只在Application中被调用了一次,单例不单例也就没什么影响了。
UncaughtExceptionHandler
un(没有)caught(接住)exception(异常)handler(处理)
@FunctionalInterface
public interface UncaughtExceptionHandler {
void uncaughtException(Thread var1, Throwable var2);
}
android本身是提供有未捕获异常处理机制的,所以这就省了很多事了,我们只需要考虑拦截实现。
在刚刚的代码中,可以看到两句看起来略有冲突的语句:
this.defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
第一句Thread.getDefaultUncaughtExceptionHandler()是获取系统默认的处理器
第二句Thread.setDefaultUncaughtExceptionHandler(this)是把我们实现的UncaughtExceptionHandler接口CrashHandler设置为系统默认的处理器
So,理一下,我们先获取了一个系统默认的处理器,然后把我们自己实现的接口设置为默认的。
首先Thread.getDefaultUncaughtExceptionHandler()维护了一个异常处理器,把这个对象赋值给defaultUncaughtExceptionHandler,这样当我们想通过系统默认处理器的时候就可以通过defaultUncaughtExceptionHandler去调用了;
然后Thread.setDefaultUncaughtExceptionHandler(this)这个方法是为了当系统检测到未捕获异常时会使用我们实现的接口
其实说起来有点啰嗦,想想a、b变量交互的逻辑应该就能清楚很多
说完这些,我们也发现了重点就是我们实现的接口uncaughtException(thread, throwable)
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
writeExceptionToSDCard(throwable); // 写入SD卡
uploadedExceptionToServer(throwable); // 上传到服务器
// 如果系统存在默认的未捕获异常处理器则让系统处理,否则自己处理
if (defaultUncaughtExceptionHandler != null) {
defaultUncaughtExceptionHandler.uncaughtException(thread, throwable);
} else {
// TODO: 2017/8/7 自己的处理
}
}







网友评论