Android Handler消息机制(4)MessageQueue

719 阅读2分钟

「这是我参与11月更文挑战的第18天,活动详情查看:2021最后一次更文挑战

Handler机制原理

3.MessageQueue

上篇文章介绍了enqueueMessage,这个方法功能是将消息插入到消息队列中。还有next方法和quit方法在本篇文章进行讲解。

next 方法从消息队列中取出一条消息并将其从消息队列中移除。

Message next() {
    //...
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        //阻塞方法Ptr是MessageQueue的一个long型成员变量,关联的是一个在C++层的MessageQueue,阻塞操作就是通过底层的这个MessageQueue来操作的;当队列被放弃的时候其变为0。
        nativePollOnce(ptr, nextPollTimeoutMillis);
​
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                //msg.target == null表示此消息为消息屏障(通过postSyncBarrier方法发送来的)
                //如果发现了一个消息屏障,会循环找出第一个异步消息(如果有异步消息的话),所有同步消息都将忽略(平常发送的一般都是同步消息)
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    //如果消息还没有到达时间,会设置阻塞时间nextPollTimeoutMillis,在下次循环开始时会调用nativePollOnce进行消息阻塞。
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    //取出消息设置mBlocked = false代表目前没有被阻塞。
                    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 {
                nextPollTimeoutMillis = -1;
            }
            if (mQuitting) {
                dispose();
                return null;
            }
            //...处理IdleHandler任务
        }
    }

nativePollOnce阻塞方法,主要是通过native层的epoll监听文件描述符的写入事件来实现的,nextPollTimeoutMillis阻塞时间会有以下三种情况:

  1. 如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
  2. 如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
  3. 如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。

总结一下,当首次进入或消息已经被处理后,mMessages为null也就是队列中没有消息。nextPollTimeoutMillis=-1,回去处理IdleHandler任务,IdleHandler ,就是 Handler 机制提供的一种,可以在 Looper 事件循环的过程中,当出现空闲的时候,允许我们执行任务的一种机制。

如果发现消息屏障回调过后面的同步消息。

如果消息没有到时,则更新nextPollTimeoutMillis,进行阻塞.

如果到时,则直接返回此消息交由Looper去处理。