本篇文章主要是讲解Android消息机制中 Message 执行的几种监控方式:
Printer 监听 Message 执行的起始时机 Observer 监听 Message 执行的起始时机并将 Message 作为参数传入 dump 方式打印消息队列中 Message 快照上面几种方式各有其优缺点及适用场景,下面我们一一进行分析(其中,Android SDK32中 Looper 的源码发生了一些变化,不过不影响阅读)。
Printer方式
对应 Looper 源码中的:
我们直接深入到 Looper 的核心方法 loopOnce() (基于SDK32的源码)进行分析:
private static boolean loopOnce(final Looper me, final long ident, final int thresholdOverride) { Message msg = me.mQueue.next(); // might block ... final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid); try { msg.target.dispatchMessage(msg); ... } ... if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } ... msg.recycleUnchecked(); return true; }
其中 msg.target.dispatchMessage() 就是我们消息分发执行的地方,而在这个执行前后都会调用 Printer.println() 方法。
所以如果我们能够将这个 Printer 对象替换成我们自定义的,不就可以监听 Message 执行和结束的时机,所幸, Looper 也确实提供了一个方法 setMessageLogging() 支持外部自定义 Printer 传入:
public void setMessageLogging(@Nullable Printer printer) { mLogging = printer; }
这个有什么用呢,比如可以用来监听耗时的 Message ,从而定位到业务代码中卡顿的代码位置进行优化, ANRWatchDog 据我所知就使用了这样的原理。
Observer方式
这个定位到 Looper 源码中就是:
可以看到这个接口提供的方法参数更加丰富,我们看下它在源码中的调用位置(精简后的代码如下):
private static boolean loopOnce(final Looper me, final long ident, final int thresholdOverride) { Message msg = me.mQueue.next(); // might block final Observer observer = sObserver; Object token = null; if (observer != null) { token = observer.messageDispatchStarting(); } try { msg.target.dispatchMessage(msg); if (observer != null) { observer.messageDispatched(token, msg); } } catch (Exception exception) { if (observer != null) { observer.dispatchingThrewException(token, msg, exception); } throw exception; } }
和上面的 Printer 调用有点相似,也是在 消息执行前、消息执行后 调用,其中执行后分为两种:
正常执行后调用 messageDispatched() ; 异常执行后调用 dispatchingThrewException() ;下面我们简单的介绍 Observer 这三个接口方法:
messageDispatchStarting() 在 Message 执行之前进行调用,并且可以返回一个 标识 来标识这条 Message 消息,这样当消息正常执行结束后,调用 messageDispatched() 方法传入这个标识和当前分发的 Message ,我们就可以建立这个标识和 Message 之间的映射关系;出现异常的时候就会调用 dispatchingThrewException() 方法,除了传入标识和分发的 Message 外,还会传入捕捉到的异常。
不过很遗憾的是, Observer 是个被 @Hide 标记的,不允许开发者进行调用,如果大家真要使用。
dump方式
这个可以打印当前消息队列中每条消息的快照信息,可以根据需要进行调用:
1 . Looper.dump() :
public void dump(@NonNull Printer pw, @NonNull String prefix) { pw.println(prefix + toString()); mQueue.dump(pw, prefix + " ", null); }
2 . MessageQueue.dump()
void dump(Printer pw, String prefix, Handler h) { synchronized (this) { long now = SystemClock.uptimeMillis(); int n = 0; for (Message msg = mMessages; msg != null; msg = msg.next) { if (h == null || h == msg.target) { pw.println(prefix + "Message " + n + ": " + msg.toString(now)); } n++; } pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked() + ", quitting=" + mQuitting + ")"); } }
很直观的可以看到,当调用 dump() 方法时会传入一个 Printer 对象实例,就会遍历消息队列 mMessages ,通过传入的 Printer 打印每条消息的内容。
其中 Message 重写了 toString() 方法:
大家可以根据需要自行使用。
查看更多关于Android消息机制中Message常用的几种监控方式的详细内容...