如何监测Android应用卡顿?这篇就够了( 二 )


所以我们可以自定义对象 , 让的日志都通过我们自定义的进行打印 , 然后收集日志信息 , 匹配 to和 to字段 , 如果在设定的某个时间内只有 to字段而没有 to字段 , 那么就说明发生了卡顿 。发生卡顿后我们就收集此时的调用栈信息 。相反如果两个字段都存在则说明应用运行的很流畅 。
字段设置给对象:
Looper.getMainLooper().setMessageLogging(new Printer() {@Overridepublic void println(String log) {Log.e("printer","==println=="+log);}});
代码中的log字段就是我们需要的和字段 , 我们监测这两个字段并收集调用栈信息将其发送到后端进行分析使用 。
那么这里其实还存在一个问题就是可能我们收集的信息不够准确 , 为什么呢?就是我们收集的调用栈信息是最后收集的 , 那么这个时候有可能卡顿已经执行完成了 , 此刻搜集到的信息有可能不是卡顿发生的关键信息 。就像OOM一样 , 它是一个随时都有可能发生的 。所以我们需要高频率的收集日志信息 , 高频率的收集对后端有一定的压力 , 而我们高频收集的信息有很大一部分也是重复的 , 所以就需要日志去重操作 。
ANR异常
ANR异常全称Not  , 即应用无响应 。如果你的应用程序有一段时间响应不够灵敏 , 系统会向用户显示一个对话框 , 这个对话框称作应用程序无响应对话框 , 用户可以选择“等待”而让程序继续运行 , 也可以选择“强制关闭” 。所以一个流畅的合理的应用程序中不能出现anr 。因为这很影响用户的使用体验 , 当然由于厂商深度定制系统的原因 , 在某些手机上发生ANR也不会弹框的 。
发生ANR到弹框在不同的组件之间时间定义是不一样的 , 按键是5秒 。前台广播10秒 , 后台广播60秒 。前台服务20秒 , 后台服务200秒 。这些数据都定义在AMS中 , 读者可以去看看 。
ANR发生执行的流程:
ANR的日志在data/anr/.txt目录下 。

如何监测Android应用卡顿?这篇就够了

文章插图
我们在线下的时候可以直接通过ADB命令来查看日志:
adb pull data/anr/.txt 你的目录
这样可以详细分析CPU、IO、锁等操作的问题所在 。
线上我们可以使用监控文件变化 , 但是这种方法在高版本系统中有权限问题 。另外一种就是使用框架 。这也是一个开源框架 , 地址: 。它的原理就是通过修改值的方式判断UI线程是否发生卡顿 。
这个库使用也非常简单 , 首先在中配置:
compile 'com.github.anrwatchdog:anrwatchdog:1.4.0'
然后在中进行初始化:
new ANRWatchDog().start();
这样就可以了 。默认检测到卡顿就直接抛异常将应用奔溃 , 我们可以复写接口来抓取堆栈信息 。
是继承之线程的 , 那么我们就看下核心方法run方法中的代码逻辑 。
【如何监测Android应用卡顿?这篇就够了】// post的操作private final Runnable _ticker = new Runnable() {@Override public void run() {_tick = 0;_reported = false;}};
@Overridepublic void run() {// 首先对线程进行重命名setName("|ANR-WatchDog|");?long interval = _timeoutInterval;while (!isInterrupted()) {boolean needPost = _tick == 0;_tick += interval;if (needPost) {// 发送post_uiHandler.post(_ticker);}?try {// 睡眠Thread.sleep(interval);} catch (InterruptedException e) {_interruptionListener.onInterrupted(e);return ;}?// If the main thread has not handled _ticker, it is blocked. ANR.if (_tick != 0 && !_reported) {//noinspection ConstantConditionsif (!_ignoreDebugger && (Debug.isDebuggerConnected() || Debug.waitingForDebugger())) {Log.w("ANRWatchdog", "An ANR was detected but ignored because the debugger is connected (you can prevent this with setIgnoreDebugger(true))");_reported = true;continue ;}?interval = _anrInterceptor.intercept(_tick);if (interval > 0) {continue;}?final ANRError error;if (_namePrefix != null) {error = ANRError.New(_tick, _namePrefix, _logThreadsWithoutStackTrace);} else {error = ANRError.NewMainOnly(_tick);}_anrListener.onAppNotResponding(error);interval = _timeoutInterval;_reported = true;}}}