MessageQueue&Message 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就拥有整个消息链表。


MessageQueue&Message Pool—消息的读写、删除和回收_第1张图片


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
//现在的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 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。也有可能新插入了一个消息唤醒了线程,继续循环判断,直到取出一个消息为止。

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 flag
                sPoolSize--;//消息池数量减少一个
                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是栈。


你可能感兴趣的:(Android)