Android源码分析之MessageQueue

  下面让我们花些时间来看看MessageQueue的具体实现,不过在分析代码之前让我们来理解下在类开头的一大段comments。

MessageQueue是比较低层的类,是持有Message(在Looper中派发)的队列,但Message不是直接添加到MessageQueue中的,

而是通过与Looper相关联的Handler来进行的。大多数情况下,你不需要显式的new它,当你setup一个Looper时,MessageQueue

会被自动创建(具体参见Looper的构造器)。

  下面我们先来看看关键字段和ctor:

    // True if the message queue can be quit.
    private final boolean mQuitAllowed;

    @SuppressWarnings("unused")
    private int mPtr; // used by native code

    Message mMessages;
    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
    private IdleHandler[] mPendingIdleHandlers;
    private boolean mQuitting;

    // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
    private boolean mBlocked;

    // The next barrier token.
    // Barriers are indicated by messages with a null target whose arg1 field carries the token.
    private int mNextBarrierToken;

    private native static int nativeInit();
    private native static void nativeDestroy(int ptr);
    private native static void nativePollOnce(int ptr, int timeoutMillis);
    private native static void nativeWake(int ptr);
    private native static boolean nativeIsIdling(int ptr);

    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

mQuitAllowed表示MessageQueue是否允许退出,系统创建的UI线程的MessageQueue是不允许的,其他客户端代码创建的都是允许的;

mPtr是native代码相关的,指向C/C++代码中的某些对象(指针),其他一些nativeXXX()相关的函数本文暂不做分析;

mMessages表示消息队列的头Head;

mIdleHandlers是IdldHandler接口的ArrayList, mPendingIdleHandlers是数组版本,在后面的代码中会将ArrayList的内容拷贝到它里面;

mQuitting表示当前队列是否处于正在退出状态;

mBlocked表示next()调用是否被block在timeout不为0的pollOnce上;

mNextBarrierToken表示下一个barrier token,barrier用target==null, arg1==token的Message对象表示;

ctor中初始化mQuitAllowd和native的mPtr指针;

  下面让我们接着看下IdleHandler callback接口:

     /**
     * Callback interface for discovering when a thread is going to block
     * waiting for more messages.
     */
    public static interface IdleHandler {
        /**
         * Called when the message queue has run out of messages and will now
         * wait for more.  Return true to keep your idle handler active, false
         * to have it removed.  This may be called if there are still messages
         * pending in the queue, but they are all scheduled to be dispatched
         * after the current time.
         */
        boolean queueIdle();
    }

    /**
     * Add a new {@link IdleHandler} to this message queue.  This may be
     * removed automatically for you by returning false from
     * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
     * invoked, or explicitly removing it with {@link #removeIdleHandler}.
     * 
     * <p>This method is safe to call from any thread.
     * 
     * @param handler The IdleHandler to be added.
     */
    public void addIdleHandler(IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }

    /**
     * Remove an {@link IdleHandler} from the queue that was previously added
     * with {@link #addIdleHandler}.  If the given object is not currently
     * in the idle list, nothing is done.
     * 
     * @param handler The IdleHandler to be removed.
     */
    public void removeIdleHandler(IdleHandler handler) {
        synchronized (this) {
            mIdleHandlers.remove(handler);
        }
    }

IdleHandler接口表示当MessageQueue发现当前没有更多消息可以处理的时候则顺便干点别的事情的callback函数(即如果发现idle了,

那就找点别的事干)。callback函数有个boolean的返回值,表示是否keep。如果返回false,则它会在调用完毕之后从mIdleHandlers

中移除。这里让我们来看一个具体的例子(实现),ActivityThread.java里的一个内部类,代码如下:

final class GcIdler implements MessageQueue.IdleHandler {
        @Override
        public final boolean queueIdle() {
            doGcIfNeeded();
            return false;
        }
    }

这是一个gc相关的IdleHandler,即如果没有更多的消息可以处理就会抽空doGcIfNeeded(),最后返回false表示不保留在mIdleHandlers

中,即用一次就扔了,只执行一遍。

  下来看一组清理,销毁相关的方法:

    @Override
    protected void finalize() throws Throwable {
        try {
            dispose();
        } finally {
            super.finalize();
        }
    }

    // Disposes of the underlying message queue.
    // Must only be called on the looper thread or the finalizer.
    private void dispose() {
        if (mPtr != 0) {
            nativeDestroy(mPtr);
            mPtr = 0;
        }
    }

dispose会在finalize被调用的时候执行,会清理掉native层的相关对象。

  接下来看MessageQueue的核心方法,next()方法,如下:

  Message next() {
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            // We can assume mPtr != 0 because the loop is obviously still running.
            // The looper will not call this method after the loop quits.
            nativePollOnce(mPtr, 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) {
                    // 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 (false) Log.v("MessageQueue", "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("MessageQueue", "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;
        }
    }

首先初始化2个接下来要用到的变量,紧接着进入无限for循环中,其某次循环主要做这么几件事情:

1. 如果nextPollTimeoutMillis != 0的话,调用Binder.flushPendingCommands();

2. 调用nativePollOnce(mPtr, nextPollTimeoutMillis);

3. 进入一个大的同步块,尝试获取一个可以处理的消息,具体做法是,记录当前时间now,初始化变量prevMsg为null,msg为mMessges;

如果msg是一个sync barrier消息,则直奔下一个asynchronous消息(这之间的所有同步消息会被本次循环忽略,也就是说遇到这种情况,

next方法会从找到的异步消息的位置开始尝试获取一个可以处理的消息并返回),同时更新prevMsg,msg的值;当退出此do...while循环

的时候msg可能为空(走到队列尾了),或者成功找到了一个这样的(异步)消息。如果是到队尾了即msg==null,则表示没更多的消息

了,设置nextPollTimeoutMillis = -1;否则当now<msg.when(msg的时间还没到),设置一个合理的等待时间,即调用

nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);当msg到了该处理的时间了,也就是说我们找到了

这样一个消息可以返回了,设置mBlocked为false,将msg从mMessages队列中取出来(类似单链表的删除操作),并执行

msg.next=null、msg.markInUse(),返回msg。如果到这一步了还没return的话,那说明还没有可以处理的消息,检查下队列是否要求

退出了,如果是执行dispose(),返回null。当Looper的loop方法看到null的message的时候会退出loop。接下来既然没消息可以处理,

那就该处理IdleHandler了。如果pendingIdleHandlerCount小于0(注意其在第一次进入for循环是被初始化为-1)且没更多的消息需要

处理,设置pendingIdleHandlerCount=mIdleHandlers.size();如果pendingIdleHandlerCount还是<=0的话,表示没有idle handler

需要执行,设置mBlocked为true,接着进入下次循环。接下来就是根据mIdleHandlers来初始化mPendingIdleHandlers。

4. 退出同步块后我们就剩下最后一件事了,那就是run Idle handlers。一个for循环用来做这就事情,在循环内如果IdleHandler没必要保留,

则会从mIdleHandlers中移除。

5. 最后重置pendingIdleHandlerCount为0(也就是4只会在第一次循环的时候执行一次),将nextPollTimeoutMillis设为0,因为当我们在

执行4的时候,新的Message可能已经到来了,所以我们需要立即开始(不需要等待)下次循环来检查。

  看完了next()方法,接下来我们来看enqueue()方法:

  boolean enqueueMessage(Message msg, long when) {
        if (msg.isInUse()) {
            throw new AndroidRuntimeException(msg + " This message is already in use.");
        }
        if (msg.target == null) {
            throw new AndroidRuntimeException("Message must have a target.");
        }

        synchronized (this) {
            if (mQuitting) {
                RuntimeException e = new RuntimeException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            }

            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                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) {
                        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;
    }

在介绍Handler的那篇文章中我们看到,许多跟message(包括runnable)相关的操作,最终都delegate给了MessageQueue的enqueue

方法。和任何方法一样,在enqueue之前,也都是对参数msg的检查,比如msg如果在使用中,或者msg的target是null,都会抛出

AndroidRuntimeException,进行完条件检查后,会进入真正的处理逻辑。接下来的操作类似在一张单链表中插入一个元素:进入同步块

1. 如果此队列处于正在退出的状态则不能在往里入队了,不能插入元素了,在这种情况下会抛出RuntimeException,然后return false,

表示失败了;

2. 接下来表示队列的状态ok,设置msg的when字段,临时变量p指向队列头;(必要的初始化,准备工作)

3. 如果队列是空的或when==0或when<p.when,也就是说要插入的这个message应该在第一个位置也就是队首,那么它将是新的Head,

将它和原先的队列连接起来;否则插入将发生在队列中间的某个位置,将msg插在第一个p的前面,p要么是null要么满足msg.when<p.when。

最后退出同步块,返回true,表示操作(入队)成功。

  接下来看2个hasMessages()方法:

  boolean hasMessages(Handler h, int what, Object object) {
        if (h == null) {
            return false;
        }

        synchronized (this) {
            Message p = mMessages;
            while (p != null) {
                if (p.target == h && p.what == what && (object == null || p.obj == object)) {
                    return true;
                }
                p = p.next;
            }
            return false;
        }
    }

    boolean hasMessages(Handler h, Runnable r, Object object) {
        if (h == null) {
            return false;
        }

        synchronized (this) {
            Message p = mMessages;
            while (p != null) {
                if (p.target == h && p.callback == r && (object == null || p.obj == object)) {
                    return true;
                }
                p = p.next;
            }
            return false;
        }
    }

这2个方法都是根据message中的字段(target、what、callback、obj)来查找相应的message。唯一要注意的就是如果h为null的话,

方法直接返回false,没有更多操作;还有一点要注意的是如果object传null的话,则忽略匹配obj字段。

  接着我们来看3个类似的removeMessages()方法,

  void removeMessages(Handler h, int what, Object object) {
        if (h == null) {
            return;
        }

        synchronized (this) {
            Message p = mMessages;

            // Remove all messages at front.
            while (p != null && p.target == h && p.what == what
                   && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycle();
                p = n;
            }

            // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && n.what == what
                        && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycle();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

    void removeMessages(Handler h, Runnable r, Object object) {
        if (h == null || r == null) {
            return;
        }

        synchronized (this) {
            Message p = mMessages;

            // Remove all messages at front.
            while (p != null && p.target == h && p.callback == r
                   && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycle();
                p = n;
            }

            // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && n.callback == r
                        && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycle();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

    void removeCallbacksAndMessages(Handler h, Object object) {
        if (h == null) {
            return;
        }

        synchronized (this) {
            Message p = mMessages;

            // Remove all messages at front.
            while (p != null && p.target == h
                    && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycle();
                p = n;
            }

            // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycle();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

这3个方法只是remove的条件不同,其主逻辑都是相同的,即从队列中删除匹配的元素。总体思想都是先从队首删除,如果删除了则队首

指向接下来的元素(排在第2位置的),重复这个过程,直到第一个不匹配的元素出现。接着从这个元素之后(after front)开始查找并删除,

方法是前一个元素指向它后一个的后一个,即p.next=nn。注意这里都是删除所有匹配的消息,而不是第一个匹配的。

  最后看下队列的quit()方法:

  void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new RuntimeException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

  private void removeAllMessagesLocked() {
        Message p = mMessages;
        while (p != null) {
            Message n = p.next;
            p.recycle();
            p = n;
        }
        mMessages = null;
    }

    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 {
                    p = n;
                    n = p.next;
                    p.recycle();
                } while (n != null);
            }
        }
    }

quit方法根据所传参数safe的值,有2种不同的退出策略,如果是safe的退出,则执行removeAllFutureMessagesLocked(),

其内部的逻辑为如果队首的元素还没到期那说明队列中其他所有的元素也都没到期,所以等同于删除所有的消息即调用

removeAllMessagesLocked();否则遍历队列找到第一个p.when>now这样的message元素p,(更新队列的尾部p.next=null,

缩短队列)从p开始一直到队列结束都是要被删掉的元素,全部删除之;如果是unsafe的退出,则所有message都直接被删除并

回收即调用removeAllMessagesLocked()。

  至此,MessageQueue的绝大部分关键代码都已分析完毕,其实你可以很明显的感觉到其内部很多操作都和单链表的操作一致,所以

理解起来并不难。我的一点小技巧,针对这种指针多的操作,你不妨在纸上画画草图,有时候会非常清晰,特别利于链表类操作的理解。

原文地址:https://www.cnblogs.com/xiaoweiz/p/3674836.html