MessageQueueMessage Pool—消息的读写、删除和回收
如有源码,版本为android6.0
消息的读写和删除对应的类是MessageQueue,这里的写也伴随着删除,回收有个抽象的概念Global Pool或者Message Pool,前者obtain()方法注释有提到,后者我感觉更形象。当然回收是在Looper的loop()方法中。
1、MessageQueue的数据结构
翻译为消息队列,但实际的数据结构是单向链表结构,下文中仍以消息队列称之;
Message中有个重要的属性Message next;next指向另外一个Message。在MessageQueue中Message A的next指向B,B的next指向C;这样 A B C就组成了单向链表结构,它们是以时间排序的,其中A为header,表示最先要处理的消息,C是最后要处理的消息,这个header是MessageQueue的一个属性,这样有了这个Header就拥有整个消息链表。
2、MessageQueue三种操作
插入、获取、删除;对应方法为enqueueMessage()、next()、removeMessage()
2.1插入消息
Handler的send()系列方法最终调用的就是enqueueMessage()
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<p.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;}
2.2获取消息
获取消息是在Looper的loop()循环中
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 iterationint 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 handlerboolean 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。也有可能新插入了一个消息唤醒了线程,继续循环判断,直到取出一个消息为止。 2.3删除消息
removeMessages(Handler h, int what, Object object) 和removeMessages(Handler h, Runnable r, Object object)
这里不做过多的解读了;简单概括还是对消息队列的遍历,找到符合条件的Message,删除这个Message,删除的Message是要被回收的,回收的消息放入消息池中。
3、消息池
消息池没有对应的类,在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中属性。
3.1提供消息—出栈
/*** 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 flagsPoolSize--;//消息池数量减少一个return m;//}}return new Message();//消息池没有消息 则new一个。}
3.2 消息回收—压栈
/*** 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++;}}}
从上面两个操作可以看出, MessageQueue中的消息和MessagePool中的Message之间的连接的方式是一样的,都是通过next指向下一个Message,但MessageQueue的数据结构是链表,但MessagePool是栈。