注册

Android消息机制中Message常用的几种监控方式

本篇文章主要是讲解Android消息机制中Message执行的几种监控方式:




  1. Printer监听Message执行的起始时机




  2. Observer监听Message执行的起始时机并将Message作为参数传入




  3. dump方式打印消息队列中Message快照





上面几种方式各有其优缺点及适用场景,下面我们一一进行分析(其中,Android SDK32中Looper的源码发生了一些变化,不过不影响阅读)。


Printer方式


对应Looper源码中的:


image.png


image.png


我们直接深入到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源码中就是:


image.png


image.png


可以看到这个接口提供的方法参数更加丰富,我们看下它在源码中的调用位置(精简后的代码如下):


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调用有点相似,也是在消息执行前、消息执行后调用,其中执行后分为两种:




  1. 正常执行后调用messageDispatched()




  2. 异常执行后调用dispatchingThrewException()




下面我们简单的介绍Observer这三个接口方法:



messageDispatchStarting()Message执行之前进行调用,并且可以返回一个标识来标识这条Message消息,这样当消息正常执行结束后,调用messageDispatched()方法传入这个标识和当前分发的Message,我们就可以建立这个标识和Message之间的映射关系;出现异常的时候就会调用dispatchingThrewException()方法,除了传入标识和分发的Message外,还会传入捕捉到的异常。



不过很遗憾的是,Observer是个被@Hide标记的,不允许开发者进行调用,如果大家真要使用,可以参考这篇文章:监控Android Looper Message调度的另一种姿势


dump方式


这个可以打印当前消息队列中每条消息的快照信息,可以根据需要进行调用:



  1. Looper.dump():

public void dump(@NonNull Printer pw, @NonNull String prefix) {
pw.println(prefix + toString());
mQueue.dump(pw, prefix + " ", null);
}


  1. 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()方法:


image.png


大家可以根据需要自行使用。


作者:长安皈故里
链接:https://juejin.cn/post/7150992884844462087
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册