一、崩溃检测原理
通过thread.setDefaultUncaughtExceptionHandler(),设置默认异常处理Handler,对未被捕获异常进行处理。
虚拟机会将没有处理的异常交给默认的UncaughtExceptionHandler处理,我们需要做的是将异常上报至服务端处理,APP端按照原本的逻辑走下去。
代码很简单:
/**
* 系统默认的handler
*/
Thread.UncaughtExceptionHandler mDefaultHandler;
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
handleException(thread, ex);
//收集数据上报之后交给默认的handler处理
if (mDefaultHandler != null) {
mDefaultHandler.uncaughtException(thread, ex);
}
}
});
我们在handleException中手机设备的硬件和软件信息将其上报给服务端。
二、ANR检测
方法一:通过向UI线程发送消息判断
Android UI线程是一个通过Looper进行消息循环的过程,UI线程不断的从消息队列中获取消息进行处理,而UI线程不会处理耗时任务,所以向UI线程发送的消息会得到及时处理,如果超过一段时间没有被处理,那说明发生了ANR。
所以实现方案是:
开一个子线程,定时想UI线程发送消息。若没有及时处理,说明主线程没有响应,认为是ANR异常。
@Override
public void run() {
while (true) {
watchDogHandler.sendEmptyMessage(MESSAGE_WATCHDOG_TIME_TICK);
try {
Thread.sleep(ACTIVITY_ANR_TIMEOUT);
} catch (InterruptedException e) {
e.printStackTrace();
}
//如果相等,说明过了ACTIVITY_ANR_TIMEOUT的时间后watchDogHandler仍没有处理消息,已经ANR了
if (timeTick == lastTimeTick) {
//todo 上报ANR异常
} else {
lastTimeTick = timeTick;
}
}
}
private class WatchDogHandler extends Handler {
@Override
public void handleMessage(Message msg) {
timeTick++;
timeTick = timeTick % Integer.MAX_VALUE;
LogUtil.i("timeTick = " + timeTick);
LogUtil.i("lastTimeTick = " + lastTimeTick);
}
}
方法二:通过监控主线程处理消息的时长判断
通过查看Looper源码知道,里面有一段很关键的代码如下:
public static void loop() {
...
for (;;) {
...
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
...
msg.target.dispatchMessage(msg);
...
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
...
}
...
}
}
从上面这段代码可以看出,loop方法在处理每一个消息前后都会打印相关的日志,那么我们可以设置自定义的logging来判断每次处理消息的耗时。
Looper.getMainLooper().setMessageLogging(new Printer() {
@Override
public void println(String x) {
//通过计算两次打印日志的时间来判断处理消息的时间是否过长,从而认为是ANR。
}
});