}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
return enqueueMessage(queue, msg, uptimeMillis);
}
sendMessageDelayed
方法主要计算了消息需要被处理的时间,如果delayMillis
为0,那么消息的处理时间就是当前时间。
然后就是关键方法enqueueMessage
。
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
msg.markInUse();
msg.when = when;
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);
}
}
return true;
}
不懂得地方先不看,只看我们想看的:
-
首先设置了
Message
的when字段,也就是代表了这个消息的处理时间 -
然后判断当前队列是不是为空,是不是即时消息,是不是执行时间when大于表头的消息时间,满足任意一个,就把当前消息msg插入到表头。
-
否则,就需要遍历这个队列,也就是
链表
,找出when小于某个节点的when,找到后插入。
好了,其他内容暂且不看,总之,插入消息就是通过消息的执行时间,也就是when
字段,来找到合适的位置插入链表。
具体方法就是通过死循环,使用快慢指针p和prev,每次向后移动一格,直到找到某个节点p的when大于我们要插入消息的when字段,则插入到p和prev之间。 或者遍历到链表结束,插入到链表结尾。
所以,MessageQueue
就是一个用于存储消息、用链表实现的特殊队列结构。
总结上述内容,延迟消息的实现主要跟消息的统一存储方法有关,也就是上文说过的enqueueMessage
方法。
无论是即时消息还是延迟消息,都是计算出具体的时间,然后作为消息的when字段进程赋值。
然后在MessageQueue中找到合适的位置(安排when小到大排列),并将消息插入到MessageQueue
中。
这样,MessageQueue
就是一个按照消息时间排列的一个链表结构。
刚才说过了消息的存储,接下来看看消息的取出,也就是queue.next
方法。
Message next() {
for (;😉 {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
}
}
}
奇怪,为什么取消息也是用的死循环呢?
其实死循环就是为了保证一定要返回一条消息,如果没有可用消息,那么就阻塞在这里,一直到有新消息的到来。
其中,nativePollOnce
方法就是阻塞方法,nextPollTimeoutMillis
参数就是阻塞的时间。
那什么时候会阻塞呢?两种情况:
- 1、有消息,但是当前时间小于消息执行时间,也就是代码中的这一句:
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
}
这时候阻塞时间就是消息时间减去当前时间,然后进入下一次循环,阻塞。
- 2、没有消息的时候,也就是上述代码的最后一句:
if (msg !&