• Android源码学习(3) Handler之MessageQueue


    消息出队

    MessageQueue封装了以单向列表实现的Message队列。在Looper循环中,通过调用MessageQueue的next()方法将队首元素出队进行处理:

      1 Message next() {
      2     // Return here if the message loop has already quit and been disposed.
      3     // This can happen if the application tries to restart a looper after quit
      4     // which is not supported.
      5     final long ptr = mPtr;
      6     if (ptr == 0) {
      7         return null;
      8     }
      9 
     10     int pendingIdleHandlerCount = -1; // -1 only during first iteration
     11     int nextPollTimeoutMillis = 0;
     12     for (;;) {
     13         if (nextPollTimeoutMillis != 0) {
     14             Binder.flushPendingCommands();
     15         }
     16 
     17         nativePollOnce(ptr, nextPollTimeoutMillis);
     18 
     19         synchronized (this) {
     20             // Try to retrieve the next message.  Return if found.
     21             final long now = SystemClock.uptimeMillis();
     22             Message prevMsg = null;
     23             Message msg = mMessages;
     24             if (msg != null && msg.target == null) {
     25                 // Stalled by a barrier.  Find the next asynchronous message in the queue.
     26                 do {
     27                     prevMsg = msg;
     28                     msg = msg.next;
     29                 } while (msg != null && !msg.isAsynchronous());
     30             }
     31             if (msg != null) {
     32                 if (now < msg.when) {
     33                     // Next message is not ready.  Set a timeout to wake up when it is ready.
     34                     nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
     35                 } else {
     36                     // Got a message.
     37                     mBlocked = false;
     38                     if (prevMsg != null) {
     39                         prevMsg.next = msg.next;
     40                     } else {
     41                         mMessages = msg.next;
     42                     }
     43                     msg.next = null;
     44                     if (DEBUG) Log.v(TAG, "Returning message: " + msg);
     45                     msg.markInUse();
     46                     return msg;
     47                 }
     48             } else {
     49                 // No more messages.
     50                 nextPollTimeoutMillis = -1;
     51             }
     52 
     53             // Process the quit message now that all pending messages have been handled.
     54             if (mQuitting) {
     55                 dispose();
     56                 return null;
     57             }
     58 
     59             // If first time idle, then get the number of idlers to run.
     60             // Idle handles only run if the queue is empty or if the first message
     61             // in the queue (possibly a barrier) is due to be handled in the future.
     62             if (pendingIdleHandlerCount < 0
     63                     && (mMessages == null || now < mMessages.when)) {
     64                 pendingIdleHandlerCount = mIdleHandlers.size();
     65             }
     66             if (pendingIdleHandlerCount <= 0) {
     67                 // No idle handlers to run.  Loop and wait some more.
     68                 mBlocked = true;
     69                 continue;
     70             }
     71 
     72             if (mPendingIdleHandlers == null) {
     73                 mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
     74             }
     75             mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
     76         }
     77 
     78         // Run the idle handlers.
     79         // We only ever reach this code block during the first iteration.
     80         for (int i = 0; i < pendingIdleHandlerCount; i++) {
     81             final IdleHandler idler = mPendingIdleHandlers[i];
     82             mPendingIdleHandlers[i] = null; // release the reference to the handler
     83 
     84             boolean keep = false;
     85             try {
     86                 keep = idler.queueIdle();
     87             } catch (Throwable t) {
     88                 Log.wtf(TAG, "IdleHandler threw exception", t);
     89             }
     90 
     91             if (!keep) {
     92                 synchronized (this) {
     93                     mIdleHandlers.remove(idler);
     94                 }
     95             }
     96         }
     97 
     98         // Reset the idle handler count to 0 so we do not run them again.
     99         pendingIdleHandlerCount = 0;
    100 
    101         // While calling an idle handler, a new message could have been delivered
    102         // so go back and look again for a pending message without waiting.
    103         nextPollTimeoutMillis = 0;
    104     }
    105 }

    当队首元素执行时间未 或 队首元素为SyncBarrier且队列中没有asynchronous的Message 或 队列为空时,会执行IdleHandler(通过addIdleHandler和removeIdleHandler进行添加和移除)的queueIdle回调或者睡眠(nativePollOnce)。nativePollOnce与OnFileDescriptorEventListener相关,底层是基于Linux的epoll实现的,具体能用来干啥笔者也并不清楚,此处就把它当作定时睡眠操作吧。

    第24行,对msg.target判空,通过postSyncBarrier发送的Message的target字段为空,即当队首Message是SyncBarrier时,会在队列中查找asynchronous的Message进行处理,而非asynchronous的Message将被阻塞。

    第54行,若mQuitting为真,则返回null,从而导致Looper退出循环,结束Looper的执行。

    消息入队

    向Handler上发送消息,最终都通过enqueueMessage放入MessageQueue队列中:

     1 boolean enqueueMessage(Message msg, long when) {
     2     if (msg.target == null) {
     3         throw new IllegalArgumentException("Message must have a target.");
     4     }
     5     if (msg.isInUse()) {
     6         throw new IllegalStateException(msg + " This message is already in use.");
     7     }
     8 
     9     synchronized (this) {
    10         if (mQuitting) {
    11             IllegalStateException e = new IllegalStateException(
    12                     msg.target + " sending message to a Handler on a dead thread");
    13             Log.w(TAG, e.getMessage(), e);
    14             msg.recycle();
    15             return false;
    16         }
    17 
    18         msg.markInUse();
    19         msg.when = when;
    20         Message p = mMessages;
    21         boolean needWake;
    22         if (p == null || when == 0 || when < p.when) {
    23             // New head, wake up the event queue if blocked.
    24             msg.next = p;
    25             mMessages = msg;
    26             needWake = mBlocked;
    27         } else {
    28             // Inserted within the middle of the queue.  Usually we don't have to wake
    29             // up the event queue unless there is a barrier at the head of the queue
    30             // and the message is the earliest asynchronous message in the queue.
    31             needWake = mBlocked && p.target == null && msg.isAsynchronous();
    32             Message prev;
    33             for (;;) {
    34                 prev = p;
    35                 p = p.next;
    36                 if (p == null || when < p.when) {
    37                     break;
    38                 }
    39                 if (needWake && p.isAsynchronous()) {
    40                     needWake = false;
    41                 }
    42             }
    43             msg.next = p; // invariant: p == prev.next
    44             prev.next = msg;
    45         }
    46 
    47         // We can assume mPtr != 0 because mQuitting is false.
    48         if (needWake) {
    49             nativeWake(mPtr);
    50         }
    51     }
    52     return true;
    53 }

    enqueueMessage通过when字段维护队列中Message的先后循序。

    第22行,当队列为空 或 when为0 或 when小于队首元素的when,则将Message放入队首。通过Handler的postAtFrontOfQueue和sendMessageAtFrontOfQueue提交的Runnable和Message满足when为0。

    消息移除

    removeMessages(Handler h, int what, Object object)、removeMessages(Handler h, Runnable r, Object object)、removeCallbacksAndMessages(Handler h, Object object)用于移除消息。这三个函数底层实现基本类似,分两步操作:1) 循环移除满足条件的Message,直到其不位于队首;2) 移除剩余的满足条件Message:

    void removeMessages(Handler h, int what, Object object) {
        if (h == null) {
            return;
        }
    
        synchronized (this) {
            Message p = mMessages;
    
            // Remove all messages at front.
            while (p != null && p.target == h && p.what == what
                    && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();
                p = n;
            }
    
            // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && n.what == what
                            && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycleUnchecked();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }
  • 相关阅读:
    内核中的内存都不分页
    SQL Server 变更数据捕获(CDC)
    FPGA视频拼接器的放大和缩小功能
    Button的Click事件与js函数的两种不同顺序触发方式
    STM32系列ARM单片机介绍
    开源ETL工具kettle--数据迁移
    hdu 2846 Repository
    LeetCode 231:Power of Two
    hdu 4628 Pieces(状态压缩+记忆化搜索)
    MongoDB 数据库下载和安装
  • 原文地址:https://www.cnblogs.com/moderate-fish/p/7639029.html
Copyright © 2020-2023  润新知