美文网首页
Handler系列--MessageQueue

Handler系列--MessageQueue

作者: 小斌_bingor | 来源:发表于2019-03-25 19:59 被阅读0次

    前言

    本系列文章,将分享与Handler相关的知识,包括Handler的结构,运作流程,各个类的作用、之间的关系


    内容提要

    本篇文章将分析MessageQueue的作用,以及主要的方法


    重要属性

    //native层消息队列的指针地址
    private long mPtr;
    // 是否允许退出消息队列
    private final boolean mQuitAllowed;
    // 消息队列尾部指针,无论做什么操作,最后都会将它指向尾部节点
    Message mMessages;
    
    

    内部类/接口

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

    native方法

    private native static void nativeDestroy(long ptr);

    销毁消息

    private native static boolean nativeIsPolling(long ptr);

    消息队列是否正在进行轮询


    重要方法

    boolean enqueueMessage(Message msg, long when)

    消息入列

    • 1.下面的代码,除了说明了消息的入列规则(请看注释),还表明了消息队列的结构,消息队列的排布规则是由队列头到队列尾,消息发送的优先程度依次递减,优先从队列尾部取消息(题外话:这样其实与队列(Queue)的定义不符,队列要求只从尾部加入,头部删除,所以我觉得消息队列的“队列”,更多属于象征意义上的,实际上,它是一个单向链表)
        boolean enqueueMessage(Message msg, long when) {
            if (msg.target == null) {
                throw new IllegalArgumentException("Message must have a target.");
            }
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }
    
            synchronized (this) {
                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;
                }
    
                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                //是否需要唤醒native队列
                boolean needWake;
                if (p == null || when == 0 || when < p.when) {
                    // 如果
                    //  1.当前message指针为空(message指针总是指向队尾,说明消息队列为空)
                    //  2.when==0(说明这是一个想立即发送的message)
                    //  3.when < p.when(即这个消息要比message指针指向的消息更早地被发送)
                    // 则把传入的message放入队尾
                    // New head, wake up the event queue if blocked.
                    msg.next = p;
                    mMessages = msg;
                    // 在next()方法里面,mBlocked可能被设为true,也就是队列阻塞了
                    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.
                    // 在Handler分析里说过,
                    //  1.正经通过Handler发送的Message,必然有target
                    //  2.API<28 的情况下,理论上Message必然是同步的
                    // 所以在一般情境下,这里needWake的值就取决于mBlocked
                    // 在next()方法里面,mBlocked可能被设为true,也就是队列阻塞了
                    needWake = mBlocked && p.target == null && msg.isAsynchronous();
                    Message prev;
                    for (; ; ) {
                        prev = p;
                        p = p.next;
                        // p == null表示已经到队列的头了
                        // when < p.when 表示p对应的消息的到期时间已经晚于要入列的msg了
                        // 任一情况,都将要入列的msg插入
                        if (p == null || when < p.when) {
                            break;
                        }
                        //只要在插入的节点至队列尾部的任一节点是异步的,都不需要唤醒native队列(其实我也没搞懂是啥意思)
                        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;
        }
    


    Message next()

    取出下一条到期的Message

       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.
            // native消息队列的指针
            final long ptr = mPtr;
            // ptr == 0 ,说明没有消息了
            // 这里是否说明,入列的消息其实会被存放到native层(至少是存一个副本)?
            // 但是并没有观察到有相关的代码
            if (ptr == 0) {
                return null;
            }
    
            int pendingIdleHandlerCount = -1; // -1 only during first iteration
            //下一次轮询的时间间隔
            int nextPollTimeoutMillis = 0;
            for (; ; ) {
                if (nextPollTimeoutMillis != 0) {
                    // Binder通信机制,知识盲区,研究了再补
                    Binder.flushPendingCommands();
                }
    
                //执行一次轮询,取出待发送的消息,如果没有可用的消息,这里会阻塞,直到被重新唤醒
                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;
    
                    // 一般情境下,不存在没有target的msg,所以这部分大概率不走
                    if (msg != null && msg.target == null) {
                        //msg不为空,但是该msg不持有Handler
                        // Stalled by a barrier.  Find the next asynchronous message in the queue.
                        do {
                            prevMsg = msg;
                            msg = msg.next;
                            //如果msg不为空且不是异步的,取下一个
                            //也就是说需要找出来一个异步msg,或者到队列最后
                        } while (msg != null && !msg.isAsynchronous());
                    }
                    if (msg != null) {
                        // 1.正常的msg
                        // 2.最终找到一个异步的msg
                        if (now < msg.when) {
                            // 如果当前msg还没到发送时间,把时间差记下来,下一次轮询会按照这个时间差等待
                            // 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;
                            //取出msg
                            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;
                    }
    
                    // 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(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.
                nextPollTimeoutMillis = 0;
            }
        }
    


    void quit(boolean safe)

    退出消息队列

    • 1.这里会判断是否允许退出(主线程的消息队列就是不允许退出的)和是否已经在执行退出了
    • 2.根据是否安全退出,会移除所有未被发送的Message/移除所有Message
    • 3.最后调用了一个native方法nativeWake(),看资料说是唤醒底层的队列,目前还没看native层的源码,下回分解
        void quit(boolean safe) {
            if (!mQuitAllowed) {
                throw new IllegalStateException("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 removeAllFutureMessagesLocked()

    移除所有未到期的message

    • 1.如果队列尾部的指针已经是未到期(p.when > now),那么按照消息队列的排队规则,后面的消息肯定都没有到期,直接removeAllMessagesLocked()全部移除
    • 2.找出到期的第一个msg,后面全部移除
        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.recycleUnchecked();
                    } while (n != null);
                }
            }
        }
    
    private void removeAllMessagesLocked()

    很简单,全部消息移除

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


    private void dispose()
    • 1.处理native层的消息队列
    • 2.这个方法必须在looper线程或者在finalizer方法里面调用
    • 3.mPtr是native层消息队列的指针,通过调用native方法nativeDestroy(mPtr),销毁消息
        private void dispose() {
            if (mPtr != 0) {
                nativeDestroy(mPtr);
                mPtr = 0;
            }
        }
    

    本篇内容到此结束,感谢收看~~

    相关文章

      网友评论

          本文标题:Handler系列--MessageQueue

          本文链接:https://www.haomeiwen.com/subject/lxchvqtx.html