Android开发经验谈

我也是醉了这都还不会,Android 消息屏障与异步消息?

2020-11-11  本文已影响0人  程序员面试秘籍

❝Android 消息机制中的 MessageQueue 可以存放三种类型的消息,普通消息、消息屏障和异步消息。其中消息屏障和异步消息搭配使用,可以达到屏蔽普通消息、优先处理异步消息的目的。❞

「目录:」

1.如何插入一个消息屏障?
2.如何删除一个消息屏障?
3.如何插入一个异步消息?
4.消息屏障对插入消息有什么影响?
5.消息屏障是如何优先处理异步消息的?
6.Framework 中哪里使用了消息屏障?

1. 如何插入一个消息屏障?

见 MessageQueue 的 postSyncBarrier 方法:

private int postSyncBarrier(long when) {
    synchronized (this) {
        final int token = mNextBarrierToken++;
        final Message msg = Message.obtain();
        msg.markInUse();
        msg.when = when;
        msg.arg1 = token;
        //按时间排序插入到队列中...
        return token;
    }
}

1.消息屏障不需分发处理,没有 target Handler,后续也会根据有无 target 来判断是否为消息屏障
2.消息屏障也是有时间戳的,并且只会对后面的消息起到屏障作用,不会影响前面的消息
3.消息屏障插入后无需唤醒线程,因为消息屏障原本的目的就是打算屏蔽消息处理的
4.插入后会返回一个 token,用于后续移除指定 token 的消息屏障
5.方法为 private,外部调用需反射

2. 如何删除一个消息屏障?

见 MessageQueue 的 removeSyncBarrier 方法:

public void removeSyncBarrier(int token) {
    synchronized (this) {
        Message prev = null;
        Message p = mMessages;
        while (p != null && (p.target != null || p.arg1 != token)) {
            prev = p;
            p = p.next;
        }
        //从队列中删除这个消息屏障...
        if (needWake && !mQuitting) {
            nativeWake(mPtr);
        }
    }
}

1.根据无 target 及 token 匹配找到对应的消息屏障
2.删除屏障后可能需要唤醒线程,是否唤醒取决于当前是否是因为消息屏障而阻塞的

3. 如何插入一个异步消息?

Message 的 setAsynchronous 为开放 API,直接调用设置即可,比如在 ViewRootImpl 中对输入事件的处理:

public void dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
    SomeArgs args = SomeArgs.obtain();
    args.arg1 = event;
    args.arg2 = receiver;
    Message msg = mHandler.obtainMessage(MSG_DISPATCH_INPUT_EVENT, args);
    msg.setAsynchronous(true); //异步消息
    mHandler.sendMessage(msg);
}

由于输入事件需要快速响应,优先级比较高,所以设置为异步消息,避免被消息屏障屏蔽掉

4. 消息屏障对插入消息有什么影响?

见 MessageQueue 的 enqueueMessage 方法:

//省略部分代码...
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) { //插入到队列头部
    msg.next = p;
    mMessages = msg;
    needWake = mBlocked;
} else { //插入到队列中间
    needWake = mBlocked && p.target == null && msg.isAsynchronous();
    Message prev;
    for (;;) {
        prev = p;
        p = p.next;
        if (p == null || when < p.when) {
            break;
        }
        if (needWake && p.isAsynchronous()) {
            needWake = false;
        }
    }
    msg.next = p;
    prev.next = msg;
}
if (needWake) { //唤醒
    nativeWake(mPtr);
}

1.如果插入到队列头部,那么只要当前线程是休眠的,就要唤醒,不管有没有消息屏障,因为消息屏障不会影响在它之前的消息
2.如果插入到队列中间且队列头消息为消息屏障,那还要判断插入的消息是不是最早的异步消息,如果是才唤醒线程。因为如果之前已经有异步消息,那说明已经对之前的异步消息做过唤醒或休眠指定时间的处理了,不用再此唤醒

5. 消息屏障是如何优先处理异步消息的?

见 MessageQueue 的 next 方法:

Message next() {
    //省略部分代码...
    for (;;) {
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
        //省略部分代码...
}

1.如果当前消息不是消息屏障,那异步消息和普通消息无异,都会按照时间排序依次执行
2.如果当前消息为消息屏障,就会去找队列中的异步消息,如果没有异步消息,就无限休眠;如果有,就根据这个异步消息的处理时间去分发处理或休眠

6. Framework 中哪里使用了消息屏障?

ViewRootImpl 中界面绘制时使用了消息屏障:

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        //插入一个消息屏障,屏蔽普通消息的处理
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    }
}

void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        //移除消息屏障
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
    }
}

与输入事件一样,界面绘制也是优先级高的消息,需要优先处理,所以这里插入消息屏障 block 其他普通消息,以达到优先处理界面绘制的目的。

原文链接:https://mp.weixin.qq.com/s?__biz=MzIzOTkwMDY5Nw==&mid=2247486257&idx=1&sn=daa26597b3e1dbd378268cb992441342&chksm=e9224a47de55c3518b8d84484c9c2aede91e427cea8f36e52a929fcefc02f51e50d013f192b5&mpshare=1&scene=23&srcid=1110jp91YbDzOTzyAji0wdrB&sharer_sharetime=1605017359550&sharer_shareid=5215d443920b86d04cceaaa42defb091#rd

上一篇下一篇

猜你喜欢

热点阅读