简单记录-Handler是如何取Message的

作为一个Android程序员,Handler一定都很熟悉了,这篇文章就不对Handler的整个流程做一个讲解了,主要讲解一下Handler从MessageQueue中是如何取出将要执行的消息的。

大家都知道Handler是将Message添加到MessageQueue中的,添加Message用的是MessageQueue的enqueueMessage方法。我们直接看下代码,代码中去除了一些部分,我们来看主要逻辑。

    boolean enqueueMessage(Message msg, long when) {
        
        synchronized (this) {
            ...
            //这里先给msg设置状态,并将演示操作的时间复制给msg中的when
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            //这里先判断一下,新msg的when和队列开头的msg(后面用p代替)的when的大小,如果
            //新msg的when小于p的when或者新msg的when为0(证明不需要延时)或者说p为null
            //(队列中还没有消息),说明新msg需要马上或者说在p前面执行,这个时候就讲新msg加入到
            //队列的开头
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                //设置是否需要唤醒
                needWake = mBlocked;
            } else {
            //这里判断一下消息队列是否需要唤醒,因为有的消息需要延时执行,所以可能存在队列阻塞,
            //等待延时消息到达时间。这里三个判断意思就是:当前队列正在阻塞,并且新msg是异步消息,
            //当前消息是一个屏障消息,那么这里就需要唤醒队列。
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
            //如果不满足以上条件,证明新msg需要在p的后面来执行,所以这里用一个循环。根据when的
            //大小找出新msg合适的位置,就是为了让新msg根据放到队列中when比自己的when大的msg前面
            //这样就可以先取出来执行。这里新msg的位置一定是在p后面的。
                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;
            }

            //如果needWake为true,那么就会调用nativeWake方法唤醒队列的阻塞
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
复制代码

这样在Message就放入到了MessageQueue中。

下面当Handler取消息的时候,其实是用Looper来取出消息的,Looper中有一个for循环,一直不断的从MessageQueue中取出消息,具体的获取Message方法是在MessageQueue的next方法中:

    Message next() {

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            ...
            //这里根据nextPollTimeoutMillis参数来判断是否需要阻塞(前面的唤醒就是唤醒这里)
            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) {
                    // 如果当前msg是一个屏障消息,通过循环找到最新的异步消息
                    //异步消息是优先执行的消息,一般界面绘制相关的msg就会是异步消息
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // 判断一下msg是否有延时操作,如果有的话奖需要等待的时间赋值给
                        //nextPollTimeoutMillis,最上面就会根据nextPollTimeoutMillis
                        //的值来判断是否需要阻塞
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        //如果msg不需要延时,则直接从队列中取出。执行后面的操作。
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    nextPollTimeoutMillis = -1;
                }

                if (mQuitting) {
                    dispose();
                    return null;
                }
                ...
                if (pendingIdleHandlerCount <= 0) {
                    //如果没有任何消息可以执行,设置mBlocked为true,证明队列处于阻塞状态
                    //下一个新消息进来的时候需要唤醒列队。
                    mBlocked = true;
                    continue;
                }

            }
            ...
        }
    }
复制代码

到这里取消息的逻辑就完成了,主要是想记录一下延时消息是如何处理的,以及队列的阻塞和唤醒是有怎样的逻辑。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享