Looper日志检测卡顿:利用Handler消息机制,给Looper设置一个Printer,计算dispatchMessage的执行时间,是否超过阈值来判断是否卡顿。
Message msg = me.mQueue.next(); // might block
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
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);
}
卡顿ANR监控是在TracePlugin
的LooperAnrTracer
public class TracePlugin extends Plugin {
private LooperAnrTracer looperAnrTracer;
@Override
public void init(Application app, PluginListener listener) {
super.init(app, listener);
looperAnrTracer = new LooperAnrTracer(traceConfig);
}
@Override
public void start() {
super.start();
Runnable runnable = new Runnable() {
@Override
public void run() {
if (sdkInt < Build.VERSION_CODES.N && willUiThreadMonitorRunning(traceConfig)){
if (!UIThreadMonitor.getMonitor().isInit()) {
try {
//初始化UIThreadMonitor
UIThreadMonitor.getMonitor().init(traceConfig);
} catch (java.lang.RuntimeException e) {
MatrixLog.e(TAG, "[start] RuntimeException:%s", e);
return;
}
}
}
//UIThreadMonitord的isAlive置为true
UIThreadMonitor.getMonitor().onStart();
//start根据配置开关开启追踪
if (traceConfig.isAnrTraceEnable()) {
//onStartTrace最终调到了自身的onAlive
looperAnrTracer.onStartTrace();
}
}
};
//主线程执行
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
runnable.run();
} else {
MatrixHandlerThread.getDefaultMainHandler().post(runnable);
}
}
}
初始化TracePlugin
时会初始化LooperAnrTracer
,start时会初始化UIThreadMonitor
(UI 线程的监视器)并start,监视器和追踪器等需要在主线程进行工作。接着看下UIThreadMonitor
的init:
UIThreadMonitor
public void init(TraceConfig config) {
//Looper 监听
LooperMonitor.register(new LooperMonitor.LooperDispatchListener(historyMsgRecorder,denseMsgTracer) {
//如果LooperMonitor处于Alive状态,才会回调dispatchStart和dispatchEnd
@Override
public boolean isValid() {
return isAlive;
}
//日志>>>>>回调 x.charAt(0) == '>' 表示事件开始
@Override
public void dispatchStart() {
super.dispatchStart();
UIThreadMonitor.this.dispatchBegin();
}
//日志<<<<<回调 表示事件结束
@Override
public void dispatchEnd() {
super.dispatchEnd();
UIThreadMonitor.this.dispatchEnd();
}
});
}
LooperMonitor
LooperMonitor
就是给Looper设置一个Printer
//主线程Looper监控
private static final LooperMonitor sMainMonitor = LooperMonitor.of(Looper.getMainLooper());
//维护一个map支持多个looper监控
public static LooperMonitor of(@NonNull Looper looper) {
LooperMonitor looperMonitor = sLooperMonitorMap.get(looper);
if (looperMonitor == null) {
looperMonitor = new LooperMonitor(looper);
sLooperMonitorMap.put(looper, looperMonitor);
}
return looperMonitor;
}
//注册监控回调
@Deprecated
static void register(LooperDispatchListener listener) {
sMainMonitor.addListener(listener);
}
//创建LooperMonitor
private LooperMonitor(Looper looper) {
Objects.requireNonNull(looper);
this.looper = looper;
//利用反射设置Printer
resetPrinter();
//空闲时Handler每分钟设置一次,避免失效
addIdleHandler(looper);
}
private synchronized void resetPrinter() {
Printer originPrinter = null;
try {
if (!isReflectLoggingError) {
originPrinter = ReflectUtils.get(looper.getClass(), "mLogging", looper);
//已经设置过了,不再设置
//LooperPrinter被不同的ClassLoader加载处理
}
} catch (Exception e) {
isReflectLoggingError = true;
}
//装饰者模式,不影响原 Printer 打印日志的逻辑,又可以获取到日志
looper.setMessageLogging(printer = new LooperPrinter(originPrinter));
}
利用反射获取mLogging对象,然后使用装饰者模式不影响原 Printer 打印日志的逻辑,又可以获取到日志。根据dispatch(x.charAt(0) == '>', x);
,即>>>>>
回调dispatchStart和<<<<<
回调dispatchEnd。
接着我们回头看LooperAnrTracer
:
LooperAnrTracer
public class LooperAnrTracer extends Tracer implements ILooperListener {
//anr上报
private final AnrHandleTask anrTask = new AnrHandleTask();
//卡顿上报
private final LagHandleTask lagTask = new LagHandleTask();
@Override
public void onAlive() {
super.onAlive();
if (isAnrTraceEnable) {
//注册LooperMonitor
LooperMonitor.register(this);
//用于发送延时消息来判断是否anr的Handler
this.anrHandler = new Handler(MatrixHandlerThread.getDefaultHandler().getLooper());
this.lagHandler = new Handler(MatrixHandlerThread.getDefaultHandler().getLooper());
}
}
@Override
public void onDead() {
super.onDead();
if (isAnrTraceEnable) {
//解注册LooperMonitor
LooperMonitor.unregister(this);
//移除消息
anrHandler.removeCallbacksAndMessages(null);
lagHandler.removeCallbacksAndMessages(null);
}
}
@Override
public boolean isValid() {
return true;
}
@Override
public void onDispatchBegin(String log) {
//发送延时5秒消息
anrHandler.postDelayed(anrTask, Constants.DEFAULT_ANR);
//发送延迟2秒消息
lagHandler.postDelayed(lagTask, Constants.DEFAULT_NORMAL_LAG);
}
@Override
public void onDispatchEnd(String log, long beginNs, long endNs) {
if (traceConfig.isDevEnv()) {
long cost = (endNs - beginNs) / Constants.TIME_MILLIS_TO_NANO;
MatrixLog.v(TAG, "[dispatchEnd] beginNs:%s endNs:%s cost:%sms", beginNs, endNs, cost);
}
//移除消息
anrHandler.removeCallbacks(anrTask);
lagHandler.removeCallbacks(lagTask);
}
}
上述anr和卡顿处理方式一样 ,在消息开始的时候通过Handler发送一个延迟上报任务Runable,结束的时候移除任务,执行时间超过阈值执行Runable。
至此,LooperAnrTracer卡顿ANR监控分析完毕
但是,有些情况的卡顿上述方案无法监控到(might block):
1.主线程空闲时,也会阻塞在MessageQueue的next方法中,无法区分卡顿还是主线程空闲。
2.View的TouchEvent中的卡顿,Touch事件大部分是从nativePollOnce直接到InputEventReceiver,然后到然后到ViewRootImpl进行分发。
3.IdeaHandler的queueIdle回调方法也无法被监控到
4.同步屏障
后续再分析。。。