Android客户端异常检测

一、崩溃检测原理

通过thread.setDefaultUncaughtExceptionHandler(),设置默认异常处理Handler,对未被捕获异常进行处理。
Android客户端异常检测_第1张图片
虚拟机会将没有处理的异常交给默认的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。
Android客户端异常检测_第2张图片
所以实现方案是:
开一个子线程,定时想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。
     }
});

你可能感兴趣的:(Android,移动开发)