在Android消息机制中,MessageQueue是实现消息传递的关键组成部分。在开始学习之前,我们可能会对Looper和MessageQueue的概念感到困惑,不知道它们是如何共同工作的。为了帮助我们更好地理解这个概念,我们将从MessageQueue的基本概念入手,逐步深入到源码层面。
首先,我们需要了解MessageQueue的主要功能。作为消息队列,最直观的问题就是消息是如何存储的,以及如何插入和删除消息。通过查阅源码,我们可以找到两个主要的方法:next()和enqueueMessage()。
next()方法用于从消息队列中取出一个Message对象。而enqueueMessage()方法则用于向消息队列中插入一个新的Message对象。这两个方法分别对应了消息的获取和插入操作,因此我们可以通过这两个方法找到消息存储的位置。
接下来,我们来看一下具体的源码实现。首先是next()方法。在这个方法中,我们可以看到有一个return msg的地方,这意味着当满足一定条件时,方法会返回当前队列中的Message对象。具体来说,如果当前线程的Looper与消息源Looper相同(即消息来自于当前线程),并且队列为空或者已经到达了有效载荷的最大数量(maxInlineSize),那么就会返回msg。这样一来,我们就可以确保只有在满足一定条件下,才会从队列中取出Message对象。
另一个关键的方法是enqueueMessage()。这个方法接收两个参数:一个Message对象和一个时间戳(when)。当调用这个方法时,会根据传入的Message对象和时间戳将其添加到消息队列中。这样一来,新的消息就被成功地插入到了队列中。
综上所述,通过对MessageQueue的源码分析,我们可以了解到消息是如何存储、插入和提取的。这为我们理解Android消息机制提供了有力的支持。
以下是重构后的代码:
```java
// 判断Message的时间,如果还没有到指定的运行时间,那么重新计算时间。
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
// 获取链表中的msg
if (prevMsg != null) {
prevMsg.next = msg.next; // 要获取的这个msg不是头节点
} else {
mMessages = msg.next; // msg是头节点
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse(); // Message回收机制
return msg;
}
```
通过上面的一段代码,我们就可以获取到Message了。在之后的文章中,我会讲讲Message的回收机制。从`next()`方法的源码可以看出,在刚刚的那段代码上面有一段这样的代码:
```java
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());
}
```
从代码的字面意思来看,就是当`msg.target == null`时,获取链表中的第一个异步的Message。而`msg == null`表示什么意思呢?
这段代码主要做了以下事情:
1. 检查传入的消息是否具有目标,如果没有则抛出异常。
2. 实现一个同步屏障方法,用于在队列中添加新的同步屏障令牌。这个目的是让队列暂停执行,因为屏障的目的就是让它暂停。
3. 在同步块中,首先获取下一个令牌并将其赋值给`token`。
4. 然后创建一个新的消息对象,并设置其标记为已使用、时间和参数。
5. 接着,遍历消息链表,找到第一个比当前时间晚的消息,并将新消息插入到该消息之后。
6. 最后返回生成的令牌。
在这个方法中,他向链表中插入了一个Message对象,但是这个Message对象是通过obtain()方法获取的,而且没有设置target。因此,我们可以得出结论,这里就是插入了target为null的Message的地方。那么为什么要这样做呢?我们先来看一下next方法中的那段让我感到困惑的代码:
```java
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()); }
msg.targer == null
enqueueMessage()
// We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); }
```
为什么要唤醒线程呢?这可能是一个需要进一步研究的问题。今天把这个问题写在这里,以后有时间再去研究研究。以前觉得MessageQueue等内部代码很难看懂,但现在多看几遍之后发现并没有想象中的那么难了。