一、Looper检测卡顿
熟悉Handler机制的话,我们会知道Looper是给线程提供处理消息能力的类,在Android Framework启动的时候,就会创建一个Main Looper即主线程对应的Looper,Looper中会维护一个MessageQueue,负责接收Handler发送过来的消息,MessageQueue是个消息队列,它是顺序取消息的,只有取完一个任务才会接着取另外一个任务。
查看Looper的源码后就可以发现我们可以使用Looper的looper方法来进行检测。因为UI更新界面都是在主线程中进行的,所以在主线程中做耗时操作可能会造成界面卡顿,looper()方法循环去从MessageQueue中去取msg,然后执行,而且是顺序执行的,那么前面一个msg还没处理完,loop()就会等待它处理完了才会再去执行下一个msg,如果前面一个msg处理很慢,那就会造成卡顿了。
二、使用Looper检测性能原理
Looper的looper方法源码如下(涉及性能部分的代码已经加粗):
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn‘t called on this thread."); } final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); // Allow overriding a threshold with a system prop. e.g. // adb shell ‘setprop log.looper.1000.main.slow 1 && stop && start‘ final int thresholdOverride = SystemProperties.getInt("log.looper." + Process.myUid() + "." + Thread.currentThread().getName() + ".slow", 0); boolean slowDeliveryDetected = false; for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // 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); } final long traceTag = me.mTraceTag; long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs; if (thresholdOverride > 0) { slowDispatchThresholdMs = thresholdOverride; slowDeliveryThresholdMs = thresholdOverride; } final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0); final boolean logSlowDispatch = (slowDispatchThresholdMs > 0); final boolean needStartTime = logSlowDelivery || logSlowDispatch; final boolean needEndTime = logSlowDispatch; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0; final long dispatchEnd; try { msg.target.dispatchMessage(msg); dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (logSlowDelivery) { if (slowDeliveryDetected) { if ((dispatchStart - msg.when) <= 10) { Slog.w(TAG, "Drained"); slowDeliveryDetected = false; } } else { if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery", msg)) { // Once we write a slow delivery log, suppress until the queue drains. slowDeliveryDetected = true; } } } if (logSlowDispatch) { showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg); } if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn‘t corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); } }
首先看到在msg.target.dispatchMessage(msg)方法前面会有一个日志打印:
final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); }
在处理完消息的时候,又会有一个结束的日志打印:
if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); }
看到这里,我们就知道可以通过Looper中的setMessageLogging()方法进行设置。
public void setMessageLogging(@Nullable Printer printer) { mLogging = printer; }
比较出名的 BlockCanary 检测性能的框架就是基于此实现的。
/** * Start monitoring. */ public void start() { if (!mMonitorStarted) { mMonitorStarted = true; Looper.getMainLooper().setMessageLogging(mBlockCanaryCore.monitor); } }
下图就是 BlockCanary 框架实现性能监测的逻辑图: