android消息机制—MessageQueue
2019-02-11 本文已影响3人
韩明泽
这篇文章只分析MessageQueue的enqueueMessage()、next()方法以及消息清除方法。
作用
- 用于将消息插入和读取
- 通过一个单链表的数据结构,维护消息列表
enqueueMessage()方法
这个方法主要是用来处理发送消息的,当Handler通过自己enqueueMessage()将消息发送到这该函数中。该函数首先会判断判断是否msg.target有Handler的引用,消息会被按着时间顺序被添加到队列中。
boolean enqueueMessage(Message msg, long when) {
// msg 必须有target也就是必须有handler
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
...
synchronized (this) {
...
msg.markInUse();
//when 表示这个消息执行的时间,队列是按照消息执行时间排序的
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 如果p为null则表示当前消息队列没有消息
msg.next = p;
//初始化头消息(改变了总是指向新的消息)
mMessages = msg;
//true代表有无消息,阻塞线程,false代表有消息,没有阻塞线程
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//将消息放到队列,消息是按照msg的when 排序
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;
}
//是否唤醒Looper等待的线程
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
Next()方法
Message next() {
...
//nextPollTimeoutMillis 表示nativePollOnce方法需要等待的时间
//nextPollTimeoutMillis=-1表示一直阻塞切不会超时
//nextPollTimeoutMillis>0 表示阻塞时长,可以理解为延迟多长时间发送消息
int nextPollTimeoutMillis = 0;//表示不会阻塞,立即执行
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//native的方法,在没有消息的时候回阻塞管道读取端,只有nativePollOnce返回之后才能往下执行
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
//从开机到现在的毫秒数
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 找不是异步而且msg.target不为空的message
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
//开机到现在的毫秒数如果小于msg.when则代表还未到发送消息的时间
if (now < msg.when) {
// 虽然有消息,但是还没有到运行的时候
//计算还有等待多久,并赋值给nextPollTimeoutMillis
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;
}
.....
.....
}
上面的next()部分代码中有部分是native相关的知识,这里不做说明,我们只了解next执行的思路。next()方法中有一个无限循环,里面调用了阻塞方法,如果有消息或者等待延迟的时间到了才不会阻塞,系统将继续执行,在获取到消息后会将消息赋值给新的变量,并将这个消息从单链表中删除。
其他方法
消息退出quit()方法
清楚消息,这个函数会在Looper中分别被quit()和quitSafely()方法调用
void quit(boolean safe) {
...
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
//移除延迟消息 Looper.quitSafely()调用
removeAllFutureMessagesLocked();
} else {
//移除全部消息 Looper.quit()调用
removeAllMessagesLocked();
}
nativeWake(mPtr);
}
}
清楚消息方法
removeAllMessagesLocked()
该方法是清楚消息列表的全部消息
private void removeAllMessagesLocked() {
Message p = mMessages; // mMessages 为消息队列的表头的消息
// 清除列表中的所有消息,recycleUnchecked()为进行回收
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
// mMessages 为 null 时Looper 的 for 循环就会结束
mMessages = null;
}
removeAllFutureMessagesLocked
该方法值清除出延迟的消息,其他非延迟消息,依旧让他执行。
private void removeAllFutureMessagesLocked() {
//获取从开机到现在的时间
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
//如果是延迟消息,那么整个消息队列都会清楚!
if (p.when > now) {
removeAllMessagesLocked();
} else {
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
// 如果当前消息是延迟消息,跳出循环
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
//剩下的消息在do while中
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}