Android 消息机制深入源码分析 [ 一 ]
Android 消息机制之 ThreadLocal 深入源码分析 [ 二 ]
Android 消息机制之 Looper 深入源码分析 [ 三 ]
Android 消息机制之 Message 与消息对象池的深入源码分析 [ 四 ]
Android 消息机制之 MessageQueue 深入源码分析 [ 五 ]
Android 消息机制之初识Handler [ 六 ]
Android 消息机制之 Handler 发送消息的深入源码分析 [ 七 ]
Android 消息机制之 MessageQueue.next() 消息取出的深入源码分析 [ 八 ]
Android 消息机制之消息的其他处理深入源码分析 [ 九 ]
Android 消息机制总结 [ 十 ]
上一章学习了消息机制中的 Message 与 Message 的消息对象池. 本章继续学习消息机制中 MessageQueue 消息队列的相关概念.
尽管 MessageQueue
叫消息队列, 但是它的内部实现却并不是用的队列, 实际上它是通过一个单链表的数据结果来维护消息列表. (单链表在插入与删除上很有优势)
MessageQueue
主要包括两个操作, 插入和读取. 但是读取操作本身会伴随着删除操作. 插入和读取对应的方法分别是 enqueueMessage
与 next
.
-
enqueueMessage
作用是往消息队列中插入一条数据. -
next
作用是从消息队列中取出一条消息并将其从消息队列中移除.next
是一个无限循环的方法, 如果消息队列中没有消息, 那么next
方法会一直阻塞, 当有新消息来的时候,next
会返回这条消息并将其从链表中移除.
消息队列中所有的 Message
都是按照时间从前往后有序排列的.
1. 成员变量
private final boolean mQuitAllowed
private long mPtr; // used by native code
Message mMessages
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
private IdleHandler[] mPendingIdleHandlers;
private boolean mQuitting;
private boolean mBlocked;
private int mNextBarrierToken;
2. IdleHandler 接口
在 MessageQueue.java 811 行 中, 有一个内部接口为 IdleHandler.
public static interface IdleHandler {
boolean queueIdle();
}
boolean queueIdle()
需要注意的是, 当消息队列中还有其他的 Delay Message (延迟消息)
, 并且这些 Message
还没到执行时间的时候, 由于线程空闲, 所以这个方法也可能会被调用, IdleHandler
也可能会被执行.
IdleHandler
就是一个简单的回调接口, 内部就是一个带有返回值的方法, 在使用的时候只需要实现该接口并加入到 MessageQueue
中就可以了.
//例:
MessageQueue messageQueue = Looper.mQueue();
messageQueue.addIdleHandler(new MessageQueue.IdleHandler(){
@Override
public boolean queueIdle(){
return false;
}
});
接着看一下 addIdleHandler
方法做了些什么.
MessageQueue.java 117行
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
- 官方翻译:
方法内部很简单, 就是三个步骤
- 做非空判断.
- 加同步锁.
- 将要添加的
IdleHandler
添加至mIdleHandlers (ArrayList)
2. MessageQueue 中的 Message 的分类
在 MessageQueue 中, Message 被分为 3 类.
- 同步消息
- 异步消息
- 障栅(Barrier)
注: 这里说的异步, 并不是说新开一个线程, 并不是这个概念. 这里说的异步消息是针对 Message
中的障栅而言. 当障栅出现的时候, 同步消息就会被阻塞, 而异步消息就不会, 所以这个异步消息仅仅只是一个 Message
的标记.
3. 添加障栅 Barrier
首先看一下 障栅的添加. 在 MessageQueu.java
中 障栅的添加方式有两种.
1. postSyncBarrier(). 代码位于 MessageQueu.java 461 行.
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
- 官方翻译:
可以看到方法内部又调用了另外一个同名有参的方法, 并传入了手机开机到现在的时间.
2. postSyncBarrier(long when). 代码位于 MessageQueu.java 465 行.
private int postSyncBarrier(long when) {
synchronized (this) {
//分析 1
final int token = mNextBarrierToken++;
//分析 2
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
//分析 3
Message prev = null;
Message p = mMessages;
if (when != 0) {
//分析 4
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
//分析 5
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
//分析 6
return token;
}
}
- 分析
- 解析
注意: 在上面的添加障栅的代码中, 可以看到 msg
这个障栅的 target
都是 null
, 自始至终都没有被赋值过, 上面也说过自有障栅的 target
才会为 null
. 这也是后面在移除障栅的时候通过这个来判断的条件之一.
4. 移除障栅
移除障栅的方法为 removeSyncBarrier()
, 代码位于 MessageQueue.java 504 行
public void removeSyncBarrier(int token) {
synchronized (this) {
Message prev = null;
//还是获取消息队列中的第一个元素
Message p = mMessages;
//遍历消息队列所有元素, 找到与 token 对应的障栅
//如果有障栅, 那么 prev 要么为 null, 表示消息队列的第一位就是障栅
//如果 prev 不为 null, 则 prev.next 为障栅, 而 p 的第一个元素为障栅
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
//是否需要唤醒
final boolean needWake;
//如果障栅不是消息队列中的第一个元素.
if (prev != null) {
//可以理解为: 将消息队列中障栅的下一个消息对象,
// 赋值给消息队列的障栅位置.达到移除的目的.
prev.next = p.next;
//因为有元素,所以不需要唤醒
needWake = false;
} else {
//如果障栅就是第一个元素,则直接把消息队列中的第一个元素指向障栅的下一个元素
mMessages = p.next;
//赋值完以后,判断消息队列中是否有消息,是否需要唤醒
needWake = mMessages == null || mMessages.target != null;
}
//将 P 回收放入消息对象池.
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
- 解析
-
MessageQueue.removeMessages(Handler h, int what, Object object)
: 从消息队列中删除所有符合指定条件的 Message -
MessageQueue.removeAllFutureMessagesLocked()
: 删除所有未来的消息. -
MessageQueue.quit(boolean safe)
: 关闭消息队列(在 Looper 篇中已经分析过) -
MessageQueue.next()
从消息队列中取出消息交给 Looper. (核心) -
MessageQueue.hasMessages(Handler h, int what, Object object)
: 查看消息是否存在
这些方法都会放到 Handler
中有关消息的发送, 取出以及其他操作的文章中. 本篇文章主要是了解一下在 MessageQueue
中对 Message
的几种分类, 以及如何添加障栅(Barrier), 移除障栅的操作. 下一章正式开始学习 消息机制中的 Handler
.