Handler的前世今生5 —— MessageQueue

MessageQueue文档
通过前面的文章,我们知道很多操作其实都是MessageQueue来负责:

  1. Looper 的loop() 中使用 MessageQueue的next() 方法;
  2. Looper的quit()中使用MessageQueuequit() 方法;
  3. Handler中的enqueueMessage() 使用MessageQueueenqueueMessage() 方法;
  4. Handler中的removeCallbacksAndMessages()使用的是MessageQueue中的removeCallbacksAndMessages方法;

其实在开发过程中,我们是不与MessageQueue直接打交道的。

But毫不夸张的讲,只有真正的理解了MessageQueue,我们才能真正地明白整个Handler通信机制的设计之美。


MessageQueue的三大功能:

  1. 消息入队操作及按照时间排序操作
  2. 消息出队列线程阻塞实现
  3. 阻塞时提供IdleHandler执行一些其它任务(肯定不能是耗时操作);
MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        // 这个就是Native层的MessageQueue.一切关于MessageQueue的操作都是围绕它展开的
        mPtr = nativeInit();
    }

1. enqueueMessage()入队操作

需要我们特别注意的是:在消息入队列的时候,直接按照when进行排序

具体的来下面的源码注释:上来肯定是要对Message 以及MessageQueue 的状态进行判断。

boolean enqueueMessage(Message msg, long when) {
        // 1. 判断消息的所有者
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        // 2. 判断消息的状态
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            // 3. 判断MessageQueue的状态
            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;
            }
            // 4. 更新消息的状态
            msg.markInUse();
            // 5. 重点:Message的when属性是在这里赋值的
            msg.when = when;
            // 6.消息队列的头节点
            Message p = mMessages;
            // 7. 是否需要唤醒线程
            boolean needWake;
        
            /**
            * 从这里开始是真正处理消息入队列。
            * 插入表头的三种情况:
            * 1. 当 头节点为空即(消息队列为空)时,
            * 2. Handler发送消息时要求直接插入到队列头部:(对应的应该是Handler的sendMessageAtFrontOfQueue()),when = 0
            * 3. 当前消息的延迟时间小于头节点的时间
            */
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                //新消息的next指针指向头节点;
                msg.next = p;
                // 更新头节点
                mMessages = msg;
                // 若当前是阻塞状态,则直接唤醒;
                needWake = mBlocked;
            } else {
                // 根据注释,我们也能看出个大概,将消息插入到队列的中间,一般情况下,不会直接唤醒的。
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    // 注意这里:这里是保证 Message 在入队列时按照 when 进行排序的关键。
                    //在入队的时候就进行排序了,而不是先入队列再sort.... 233333
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }

                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // 这里的mPtr,其实是C++层次的一个MessageQueue.当mPtr != 0 ,即 表示消息队列没有退出。
            // 如果是阻塞,就是调用native方法nativeWake进行唤醒。 
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        // 入队成功
        return true;
    }

2. IdleHandler

我们都知道Looper在loop(),如果没有消息则会wait,进行阻塞(而且我们知道,真正的阻塞逻辑是在MessageQueue的next()方法中实现的)。但是这样阻塞着,是不是有点浪费资源呢?

不管咱们觉的,Android的设计者也认为这是一种浪费,所以MessageQueue还担当了另一个角色:当线程阻塞时,给我们提供了IdleHandler 用来在线程空闲时处理任务。
MessageQueue的IdleHandler说明

 /**
     * Callback interface for discovering when a thread is going to block
     * waiting for more messages.
     */
    public static interface IdleHandler {
        boolean queueIdle();
    }

这里先提到IdleHandler是因为next()方法在出现阻塞时,要对应的去处理一些IdleHandler事件。

整个MessageQueue的阻塞逻辑都在这个next()方法里面。

3. next()出队操作

根据上面的内容,我们知道next()方法有以下作用:

  1. 返回Message;
  2. 阻塞;
  3. 阻塞时处理IdleHandler;
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();
            }
            
            // 注意这里:根据参数nextPollTimeoutMillis,Native层的MessageQueue决定是否阻塞
            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) {
                    // 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;
                }


            /********************处理IdleHandler的逻辑************************/

                // 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.
            // 运行IdleHandler...
            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.
            // 当调用一个IdleHandler时,一个新的Message可能被传递过来,
            // 所以 需要回过头重新判断一下消息的状态;
            nextPollTimeoutMillis = 0;
        }
    }

注意观察:代码中只有两处给我们给我们返回null。都是根据MessageQueue 中的状态给我们的。这也证实了Looper里面的当message为空时,采用return 退出循环 。

真正的重点在于:

nativePollOnce(ptr, nextPollTimeoutMillis); 阻塞逻辑就在这里
阻塞方法,主要是通过native层的epoll监听文件描述符的写入事件来实现的。
如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。

推荐阅读 深入理解MessageQueue

至于后面的退出移除操作,就不必多讲啦.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值