Android MessageQueue

01.MessageQueue作用介绍

  • MessageQueue,主要包含2个操作:插入和读取。
    • 读取操作会伴随着删除操作,插入和读取对应的方法分别为enqueueMessage和next,其中enqueueMessage的作用是往消息队列中插入一条消息,而next的作用是从消息队列中取出一条消息并将其从消息队列中移除。
  • 虽然MessageQueue叫消息队列,但是它的内部实现并不是用的队列。
    • 实际上它是通过一个单链表的数据结构来维护消息列表,单链表在插入和删除上比较有优势。

02.MessageQueue的定义

  • 通过源码我们可以知道,MessageQueue维护了一个消息列表。Messgae并不是直接添加到MessageQueue中,而是通过和Looper相关联的Handler来添加的。在当前线程中可以通过调用Looper.myQueue()方法来获取当前线程的MessageQueue。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /**
    * Low-level class holding the list of messages to be dispatched by a
    * {@link Looper}. Messages are not added directly to a MessageQueue,
    * but rather through {@link Handler} objects associated with the Looper.
    *
    * <p>You can retrieve the MessageQueue for the current thread with
    * {@link Looper#myQueue() Looper.myQueue()}.
    */
    public final class MessageQueue

03.enqueueMessage()源码

  • 源码如下所示

    • 在Message的源码中定义了一个成员属性target,其类型为Handler。由上面enqueuMessage的源码,我们可以看到,当Message没有处理其的Handler或该Message正在被处理的时候,都不能正常进入MessageQueue,这一点也是很容易理解的。当线程处于死亡状态的时候,Message会被回收掉,而不再进入该线程对应的MessageQueue中。否则,一切正常,enqueMessage就执行单链表的插入操作,将Message插入到MessageQueue中。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    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;
    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;
    }

04.next()方法源码分析

  • next方法源码如下所示

    • 在 MessageQueue 中消息的读取其实是通过内部的 next() 方法进行的,next() 方法是一个无限循环的方法。
    • 如果消息队列中没有消息,则该方法会一直阻塞,
    • 当有新消息来的时候 next() 方法会返回这条消息并将其从单链表中删除。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    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();
    }

    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;
    }

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