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

2209次阅读  |  发布于2年以前

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

  1. Printer监听Message执行的起始时机
  2. Observer监听Message执行的起始时机并将Message作为参数传入
  3. 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调用有点相似,也是在消息执行前、消息执行后调用,其中执行后分为两种:

  1. 正常执行后调用messageDispatched()
  2. 异常执行后调用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()方法:

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

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8