美文网首页
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