如有源码,版本为android6.0
消息的读写和删除对应的类是MessageQueue,这里的写也伴随着删除,回收有个抽象的概念Global Pool或者Message Pool,前者obtain()方法注释有提到,后者我感觉更形象。当然回收是在Looper的loop()方法中。
翻译为消息队列,但实际的数据结构是单向链表结构,下文中仍以消息队列称之;
Message中有个重要的属性Message next;next指向另外一个Message。在MessageQueue中Message A的next指向B,B的next指向C;这样 A B C就组成了单向链表结构,它们是以时间排序的,其中A为header,表示最先要处理的消息,C是最后要处理的消息,这个header是MessageQueue的一个属性,这样有了这个Header就拥有整个消息链表。
插入、获取、删除;对应方法为enqueueMessage()、next()、removeMessage()
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {//同步锁,插入、删除和获取都是用this同步,说明同一时间内,三个操作只能执行一个,所以消息链表是线程安全滴。
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;//队的条件就这个when,意思距离处理的时间,数值越大排队越靠后。
Message p = mMessages;//mMessages就是队列的头
boolean needWake;
if (p == null || when == 0 || when < p.when) {//p==null说明队列为空,when==0意思是现在就要处理,没有任何延迟,when
//现在的header还要早处理。
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;//换新header,新的header的next指向原来的header。
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) {//p==null说明到了队列的尾部
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;
}
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
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) {//容错? 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) {
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;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
这个方法意思是说:取链表的header,如果这个消息时间不满足,线程就阻塞,等时间到了就取出这个消息,然后第二个消息作为header。也有可能新插入了一个消息唤醒了线程,继续循环判断,直到取出一个消息为止。
消息池没有对应的类,在SDK中以global pool称呼。消息池中的消息数据结构是栈。
private static final Object sPoolSync = new Object();// 消息池操作,同步锁类
private static Message sPool;// 消息池的栈顶
private static int sPoolSize = 0;//消息池的消息个数
private static final int MAX_POOL_SIZE = 50;//消息池的消息个数上限
上面是Message中属性。
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {//提供和回收 用的是同一个同步锁
if (sPool != null) {//消息池中 栈顶没数据则消息池为空
Message m = sPool;
sPool = m.next;//第一个消息出栈,第二个消息作为栈顶
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;//消息池数量减少一个
return m;//
}
}
return new Message();//消息池没有消息 则new一个。
}
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
//清空消息
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {//线程同步
if (sPoolSize < MAX_POOL_SIZE) {//栈中消息数量是有限制的
next = sPool;//压栈,原来栈顶的作为本消息的next,即第二个消息
sPool = this;
sPoolSize++;
}
}
}