面试官:如何提高Message的优先级 | 创作者训练营第二期

7,697 阅读2分钟

面试经历

面试官:看你简历上写着看过 Handler 机制源码,说下如何提高Message的优先级?


内心分析中:

首先,我们先分析下,这个 Message 是由 Handler 进行发送,然后添加到 MessageQueue 中,Looper 遍历 MessageQueue 获取 Message 出来执行。

而 MessageQueue 是通过时间对 Message 进行排序的,若想提高 Message 的优先级,那就是要把 Message 排在 MessageQueue 的前面,这样就会优先执行了。

而我们常用的发送 Message 的方法有:

  • sendMessage
  • sendEmptyMessage
  • post
  • sendMessageDelayed
  • sendEmptyMessageDelayed
  • postDelayed

其中前三个是没有延迟的,后三个可以添加延迟。既然前三个没有延迟,那么一般而言就会先排在 MessageQueue 的前面。

这个问题如此简单。


答:使用sendMessagesendEmptyMessagepost发送 Message 能够使得 Message 排在 MessageQueue 前面,所以用这三个方法能够提高 Message 的优先级。


面试官:不是。


虽然知道自己面试跪了,但是还是要带着以下心情研究下,到底是哪里出问题了?

重新分析流程

首先,我们要确认下,Message 在 MessageQueue 的排序确实是以时间进行排序。

MessageQueue 插入 Message 的方法:enqueueMessage(Message msg, long when),when 其实就是延迟的时间。

    boolean enqueueMessage(Message msg, long when) {
        ...

        synchronized (this) {
            ...
            msg.when = when;
            Message p = mMessages;
            ...
            if (...) {
                ...
            } else {
                ...
                Message prev;
                //核心在这里,对 MessageQueue 进行遍历
                //以 when 的值从小到大进行排序
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    ...
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
            ...
        }
        return true;
    }

那我们看看 sendMessage(Message msg)的 when 值是多少:

    public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg, 0);
    }

sendMessage内部其实是调用了sendMessageDelayed方法,延迟时间为 0。

    public final boolean sendMessageDelayed(Message msg, long delayMillis){
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

sendMessageDelayed内部调用的是sendMessageAtTime方法,这里的时间为 SystemClock.uptimeMillis() + 延迟时间,也就是SystemClock.uptimeMillis() + 0

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

sendMessageAtTime内部调用了enqueueMessage

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

enqueueMessage里面调用了queue.enqueueMessage,也就是我们最开始分析的MessageQueue 插入 Message 的方法。

总结来说,通过sendMessage(Message msg)发送 Message,其 when 并不是为 0,而是SystemClock.uptimeMillis() + 0

SystemClock.uptimeMillis()表示系统开机到当前的时间总数,单位是毫秒。

em...

那有没有办法把 when 改为 0。这样的话,发送的 Message 消息就会一定排在 MessageQueue 前面了,因为 0 是最小的了。(当然,总不可能出现负数吧?😂)

寻找答案

我们再看下 Handler 发送 Message 消息有什么方法:

嗯?这个方法没有见过:

    public final boolean sendMessageAtFrontOfQueue(Message msg) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, 0);
    }

sendMessageAtFrontOfQueue 内部调用的是enqueueMessage

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

What?uptimeMillis 为 0 !! 这个不就是我们所想要的吗?

先淡定,send 和 post 方法相对应的,所以 post 应该也有一个这样的方法:

    public final boolean postAtFrontOfQueue(Runnable r){
        return sendMessageAtFrontOfQueue(getPostMessage(r));
    }

好了,结果终于出来了!!!

使用以下方法可以提高Message的优先级:

  • sendMessageAtFrontOfQueue
  • postAtFrontOfQueue

新知识,GET!

猜你喜欢