本篇文章主要是讲解Android消息机制中
Message
执行的几种监控方式:
Printer
监听Message
执行的起始时机Observer
监听Message
执行的起始时机并将Message
作为参数传入dump
方式打印消息队列中Message
快照
上面几种方式各有其优缺点及适用场景,下面我们一一进行分析(其中,Android SDK32中Looper
的源码发生了一些变化,不过不影响阅读)。
对应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
据我所知就使用了这样的原理。
这个定位到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
标记的,不允许开发者进行调用,如果大家真要使用。
这个可以打印当前消息队列中每条消息的快照信息,可以根据需要进行调用:
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()
方法:
大家可以根据需要自行使用。
Copyright© 2013-2019