android消息机制之消息队列(MessageQueue)

374 阅读2分钟

「这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战」。

MessageQueue数据结构

MessageQueue的底层实现是单链表,我们可以查看源代码

image.png MessageQueue的每个节点是Message型数据,继续查看Message的结构

image.png 会发现里面有个next引用,由此可知MessageQueue应该是链表结构

Message的主要方法

enqueueMessage

这个方法的目的也很简单,就是将Message插入到单链表中,其它的都是对msg进行一些规范检测,比如msg的target不能为空,如果为空msg不知道将要交给哪个线程(handler)进行处理

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }

    synchronized (this) {
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        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;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            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.
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            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;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

next方法

next方法会取出队列中的消息并且把节点删除

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

        ...
            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;//1
            }

            // Process the quit message now that all pending messages have been handled.
            
            ...
    }
}

对于next这里省略了一些代码,根据这段可以发现如果当消息队列为空时,nextPollTimeoutMullis会赋值为-1,下次循环时会调用Binder.flushPendingCommands(),我们进入这个方法,去看下这个方法的简介

  • Flush any Binder commands pending in the current thread to the kernel
  • driver. This can be
  • useful to call before performing an operation that may block for a long
  • time, to ensure that any pending object references have been released
  • in order to prevent the process from holding on to objects longer than
  • it needs to.

个人理解是将当前进程的所有Binder命令刷入内核,之所以要全部刷入内核是因为当前message已经全部处理,当前线程即将进入阻塞状态,所以需要在这里将所有的binder任务进行提交。