HardBirch

MessageQueue源码解析

时间:12-11-03 栏目:安卓源码解析与小应用 作者:张飞不张,文采横飞 评论:11 点击: 5,631 次

 上午,刚刚粗略的看完了handler的源码,与其相关类looper类和MessageQueue类也一并看下。    

    先来看Messagequeue,首先是类介绍:

Low-level class holding the list of messages to be dispatched by a Looper. Messages are not added directly to a MessageQueue, but rather through MessageQueue.IdleHandler objects associated with the Looper.

You can retrieve the MessageQueue for the current thread with Looper.myQueue().

 保存消息列表的低级别类,消息由Looper对象派发。消息并不是直接添加到MessageQueue中的,而是通过与Looper对象关联的MessageQueue.IdleHandler对象添加。

     调用Looper.myQueue方法可以获取当前线程的MessageQueue

    再来看一下类中的变量:

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

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

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

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

  1. Message的实例
  2. 存放IdleHandler对象的一个ArrayList
  3. 一个IdleHandler数组
  4. 一个boolean值,判断Thread是否退出
  5. 一个boolean值,判断是否允许退出
  6. 一个boolean值,判断next()是否阻塞等待一个非零超时的pollOnce()
  7. 下面的就是native code了。本文不做详解。
前面提到Message不是直接添加到MessageQueue里的,是通过IdleHandler接口进行Message添加,所以先看下IdleHandler:

/**
     * 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();
    }

一个用于在一个线程为了等待更多消息阻塞时的接口。当消息队列为空,等待更多消息时,执行这个queueIdle函数,如果返回真,继续保持你的idlehandler的活跃状态,如果返回值为false,那么就会从应用程序中移除这个IdleHandler,否则的话就会在应用程序中继续维护着这个IdleHandler,下次空闲时仍会再执会这个IdleHandler。但是如果消息队列里面有消息,但是他们的执行时间在当前时间之后,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 final void addIdleHandler(IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }


    向消息队列中添加一个新的MessageQueue.IdleHandler。当调用IdleHandler.queueIdle()返回false时,此MessageQueue.IdleHandler会自动的从消息队列中移除。或者调用removeIdleHandler(MessageQueue.IdleHandler)也可以从消息队列中移除MessageQueue.IdleHandler
这个方法是线程安全的。


有添加,就有删除:
/**
     * 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 final void removeIdleHandler(IdleHandler handler) {
        synchronized (this) {
            mIdleHandlers.remove(handler);
        }
    }

    从队列中移除之前调用addIdlehandler(MessageQueue.IdleHandler)添加的MessageQueue.IdleHandler。如果handler不在当前的空闲列表,不做任何事。

看一下它的构造方法:
MessageQueue() {
        nativeInit();
    }

调用native代码,完成初始化。

重写了类的销毁:
@Override
    protected void finalize() throws Throwable {
        try {
            nativeDestroy();
        } finally {
            super.finalize();
        }
    }

也是调用native代码,完成类的销毁。

下面就是获取下一个Message方法:
final Message next() {
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;

        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            nativePollOnce(mPtr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                final Message msg = mMessages;
                if (msg != null) {
                    final long when = msg.when;
                    if (now >= when) {
                        mBlocked = false;
                        mMessages = msg.next;
                        msg.next = null;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    } else {
                        nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
                    }
                } else {
                    nextPollTimeoutMillis = -1;
                }

                // If first time, then get the number of idlers to run.
                if (pendingIdleHandlerCount < 0) {
                    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;
        }
    }

它在取Message来进行处理时通过判断MessageQueue里面的Message是否符合时间要求来决定是否需要把Message取出来做处理,通过这种方式做到消息的定时处理。所以调用这个函数的时候,有可能会让线程进入等待状态。什么情况下,线程会进入等待状态呢?两种情况,一是当消息队列中没有消息时,它会使线程进入等待状态;二是消息队列中有消息,但是消息指定了执行的时间,而现在还没有到这个时间,线程也会进入等待状态。消息队列中的消息是按时间先后来排序的。

先看一下两个参数的意思:pendingIdleHandlerCount:空闲的handler个数。只有在第一次循环的时候值为-1。nextPollTimeoutMillis:字面理解下次轮询时间,如果当前消息队列中没有消息,它要等待的时候,for循环开始时,传入的值为0,表示不等待。
在for循环内部
 if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

如果nextPollTimeoutMillis值不为0,则等待消息。然后执行下面语句是看看当前消息队列中有没有消息:

 nativePollOnce(mPtr, nextPollTimeoutMillis);

nativePollOnce返回后,next函数将从mMessages中提取一个消息。也就是说,要让nativePollOnce返回,至少要添加一个消息到消息队列,否则nativePollOnce不过是做了一次无用功罢了。
如果nativePollOnce在Native层等待,就表明Native层也可以投递Message(消息,为了适应业界的习惯本书沿用英文,必要时才用中文,其他词同此),但是从Message类的实现代码上看,该类和Native层没有建立任何关系(即Native层不太可能去构造Java层的Message对象并把它插入到Java层的Message队列中)。那么nativePollOnce在等待什么呢?
 对于上面的问题,相信有些读者心中已有了答案:nativePollOnce除了等待Java层来的Message,还在Native层做了大量的工作。

然后再看synchronized内部代码:
synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                final Message msg = mMessages;
                if (msg != null) {
                    final long when = msg.when;
                    if (now >= when) {
                        mBlocked = false;
                        mMessages = msg.next;
                        msg.next = null;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    } else {
                        nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
                    }
                } else {
                    nextPollTimeoutMillis = -1;
                }

                // If first time, then get the number of idlers to run.
                if (pendingIdleHandlerCount < 0) {
                    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);
            }


先尝试检索出下一个message,如果存在就返回。如果message为空,nextPollTimeoutMillis就赋值为-1.就必须等待下一次消息。如果不为空,就判断时间是否可以处理这个消息,如果符合条件,把消息传给looper处理。否则,算出需要等待时间,等待到该时间,然后执行
// If first time, then get the number of idlers to run.
                if (pendingIdleHandlerCount < 0) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount == 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

如果是第一次,得到闲置的idler handler去运行。如果没有闲置的idlehandler,阻塞,然后继续循环等待更多消息。

if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);

把注册在mIdleHandlers中的IdleHandler取出来,放在mPendingIdleHandlers数组中去。
    

    接下来就是执行这些注册了的IdleHanlder了:
// 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);
                    }
                }
            }

运行空闲处理器。我们只能达到这个代码块在第一次迭代。执行完这些IdleHandler之后,线程下次调用nativePollOnce函数时,就不设置超时时间了,因为,很有可能在执行IdleHandler的时候,已经有新的消息加入到消息队列中去了,因此,要重置nextPollTimeoutMillis的值:

 // 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;

再接下来就是插入操作:

final boolean enqueueMessage(Message msg, long when) {
        if (msg.isInUse()) {
            throw new AndroidRuntimeException(msg
                    + " This message is already in use.");
        }
        if (msg.target == null && !mQuitAllowed) {
            throw new RuntimeException("Main thread not allowed to quit");
        }
        final boolean needWake;
        synchronized (this) {
            if (mQuiting) {
                RuntimeException e = new RuntimeException(
                    msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            } else if (msg.target == null) {
                mQuiting = true;
            }

            msg.when = when;
            //Log.d("MessageQueue", "Enqueing: " + msg);
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked; // new head, might need to wake up
            } else {
                Message prev = null;
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
                msg.next = prev.next;
                prev.next = msg;
                needWake = false; // still waiting on head, no need to wake up
            }
        }
        if (needWake) {
            nativeWake(mPtr);
        }
        return true;
    }

if (msg.isInUse()) {
            throw new AndroidRuntimeException(msg
                    + " This message is already in use.");
        }
        if (msg.target == null && !mQuitAllowed) {
            throw new RuntimeException("Main thread not allowed to quit");
        }

     如果msg正在被使用,或者msg对应的handler为空或者不允许退出,均报异常。

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

     是否正在退出,如果退出,返回false,如果msg对应的handler为空,把正在退出设为true。

Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked; // new head, might need to wake up
            } else {
                Message prev = null;
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
                msg.next = prev.next;
                prev.next = msg;
                needWake = false; // still waiting on head, no need to wake up
            }

如果p为空,或者when=0或者插入时间小于p.when时,就会把传入的消息插入到队列头(这也就解释了上篇文章中提到的问题,when=0时,插入队列头部),否则,插入队尾。

接下来是删除。
final boolean removeMessages(Handler h, int what, Object object,
            boolean doRemove) {
        synchronized (this) {
            Message p = mMessages;
            boolean found = false;

            // Remove all messages at front.
            while (p != null && p.target == h && p.what == what
                   && (object == null || p.obj == object)) {
                if (!doRemove) return true;
                found = true;
                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)) {
                        if (!doRemove) return true;
                        found = true;
                        Message nn = n.next;
                        n.recycle();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }

            return found;
        }
    }

    final void removeMessages(Handler h, Runnable r, Object object) {
        if (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;
            }
        }
    }

最后一个删除:

final void removeCallbacksAndMessages(Handler h, Object object) {
        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;
            }
        }
    }

                                                                                       

声明: 本文由( 张飞不张,文采横飞 )原创编译,转载请保留链接: MessageQueue源码解析

MessageQueue源码解析:目前有11 条留言

  1. 11楼
    uylong:

    这个好像比较少用到吧。

    2012-11-07 22:20 [回复]
  2. [reply]uylong[/reply]
    写程序的时候,是很少用到,但是要深入理解handler机制的时候,要了解这个Messagequeue。

    2012-11-07 22:58 [回复]
  3. 9楼
    woyaowenzi:

    "前面提到Message不是直接添加到MessageQueue里的,是通过IdleHandler接口进行Message添加,所以先看下IdleHandler:"
    没太明白什么意思,Message的添加和IdleHandler没啥关系啊。

    2012-11-09 12:51 [回复]
  4. [reply]woyaowenzi[/reply]
    首先,在类说明中:消息并不是直接添加到MessageQueue中的,而是通过与Looper对象关联的MessageQueue.IdleHandler对象添加。

    2012-11-09 13:15 [回复]
  5. [reply]woyaowenzi[/reply]
    Messages并不是直接加到一个MessageQueue中,而是通过MessageQueue.IdleHandler关联到Looper。是不是这样翻译更能容易理解一些。

    2012-11-09 13:33 [回复]
  6. 6楼
    woyaowenzi:

    [reply]aomandeshangxiao[/reply]
    消息链不是由enqueueMessage给串连起来的么?
    这个IdleHandler只是说线程在空闲的时候能够给注册过这个接口的人一个回调,让其它人可以在这个空闲的时间里干点其它的事情。

    2012-11-09 14:29 [回复]
  7. 5楼
    woyaowenzi:

    [reply]aomandeshangxiao[/reply]
    擦。。。怎么又出来了。

    2012-11-09 14:38 [回复]
  8. 4楼
    woyaowenzi:

    [reply]aomandeshangxiao[/reply]
    刚刚回复了一下,刷新了下界面,不知道为啥莫名奇妙的给丢失了。还得重新敲一遍。
    我的理解是,Message链的建立是由enqueueMessage函数进行操作的。而这个IdleHandler接口只是允许用户注册一个回调,当线程将要处于空闲(blocked)状态时,提前给用户一个回调,用户可以利用这个空闲时间干点其它事情。当然,最好不要干耗时的操作。

    2012-11-09 14:38 [回复]
  9. [reply]woyaowenzi[/reply]
    嗯,谢谢,学习了。。。

    2012-11-09 15:00 [回复]
  10. 板凳
    woyaowenzi:

    [reply]aomandeshangxiao[/reply]
    共同学习,共同学习,呵呵。博主真是很厉害,貌似毕业时间不长吧,就会得如此多,真是强大啊。

    2012-11-09 16:14 [回复]
  11. [reply]woyaowenzi[/reply]
    呵呵,菜鸟一个。。。

    2012-11-09 16:25 [回复]

发表评论


QQ群互动

Linux系统与内核学习群:194051772

WP建站技术学习交流群:194062106

魔豆之路QR

魔豆的Linux内核之路

魔豆的Linux内核之路

优秀工程师当看优秀书籍

优秀程序员,要看优秀书!

赞助商广告

友荐云推荐