概述
Android 消息机制主要指的是 Handler 的运行机制及其所依赖的 MessageQueue 和 Looper 的工作过程,Handler、MessageQueue、Looper组成一个相互联系的整体。本文先从 MessageQueue 的源码来说明其实现原理。
MessageQueue 原理
MessageQueue ,顾名思义,意为消息队列,其操作主要有插入和读取。插入对应的方法为 enqueueMessage()
,即往消息队列中插入一条消息,而读取对应next()
,该方法会从消息队列中取出一条消息并将其从消息队列中删除。虽然 MessageQueue 的名字包含队列(Queue),但是其底层实现采用的是单链表,这是因为链表在插入和删除方面的性能好。下面看其源码实现。
boolean enqueueMessage(Message msg, long when) {
... ...//省略
synchronized (this) {
if (mQuitting) {//如果中止了,直接返回
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
enqueueMessage()中,Message 对象 p 代表下一个消息,对于新进来的消息,如果满足条件: p == null || when == 0 || when < p.when
,那么就将它插在消息列表的最前面;否则,就按照消息触发的时间( when 字段)来插入消息。也就是说,消息的插入是按照时间顺序从小到大进行的。
接下来在看看获取消息的 next()
方法:
Message next()
.....//省略
int pendingIdleHandlerCount = -1; // -1 only during first iteration
// 1.如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
// 2.如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
// 3.如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时)
// 如果期间有程序唤醒会立即返回。
int nextPollTimeoutMillis = 0;
//死循环,但是拿到消息就return了
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 重要:调用到底层native的 MessageQueue
// nextPollTimeoutMillis为-1,说明没有消息需要处理,在 native 中阻塞,
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;
//如果target==null,那么它就是屏障,需要循环遍历,一直往后找到第一个异步的消息
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());
}
if (msg != null) {
//如果有消息需要处理,先判断时间有没有到,如果没到的话设置一下阻塞时间,
//场景如常用的postDelay
if (now < msg.when) {
//计算出离执行时间还有多久赋值给nextPollTimeoutMillis,
//表示nativePollOnce方法要等待nextPollTimeoutMillis时长后返回,nextPollTimeoutMillis 不为 -1
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 获取到消息
mBlocked = false;
//链表操作,获取msg并且删除该节点
if (prevMsg != null)
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
//返回拿到的消息
return msg;
}
} else {
//没有消息,nextPollTimeoutMillis复位
nextPollTimeoutMillis = -1;
}
.....//省略
}
先说一下,MessageQueue 的数据结构为一个单向链表,Message 对象有个 next 字段保存列表中的下一个,MessageQueue 中的 mMessages 保存链表的第一个元素。
可以看到,next()
是一个无限循环的方法,读取消息时如果有消息就将该消息从消息列表中移除并返回该消息,否则就一直阻塞。
参考: