android原始碼解析--MessageQueue

傲慢的上校發表於2012-11-03

 上午,剛剛粗略的看完了handler的原始碼,與其相關類looper類和MessageQueue類也一併看下。    


       先來看Messagequeue,首先是類介紹:

Low-level class holding the list of messages to be dispatched by a Looper. Messages are not added directly to a MessageQueue, but rather through MessageQueue.IdleHandler objects associated with the Looper.

You can retrieve the MessageQueue for the current thread with Looper.myQueue().


 儲存訊息列表的低階別類,訊息由Looper物件派發。訊息並不是直接新增到MessageQueue中的,而是通過與Looper物件關聯的MessageQueue.IdleHandler物件新增。

     呼叫Looper.myQueue方法可以獲取當前執行緒的MessageQueue

    再來看一下類中的變數:

Message mMessages;
    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
    private IdleHandler[] mPendingIdleHandlers;
    private boolean mQuiting;
    boolean mQuitAllowed = true;

    // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
    private boolean mBlocked;

    @SuppressWarnings("unused")
    private int mPtr; // used by native code
    
    private native void nativeInit();
    private native void nativeDestroy();
    private native void nativePollOnce(int ptr, int timeoutMillis);
    private native void nativeWake(int ptr);

  1. Message的例項
  2. 存放IdleHandler物件的一個ArrayList
  3. 一個IdleHandler陣列
  4. 一個boolean值,判斷Thread是否退出
  5. 一個boolean值,判斷是否允許退出
  6. 一個boolean值,判斷next()是否阻塞等待一個非零超時的pollOnce()
  7. 下面的就是native code了。本文不做詳解。
前面提到Message不是直接新增到MessageQueue裡的,是通過IdleHandler介面進行Message新增,所以先看下IdleHandler:

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

一個用於在一個執行緒為了等待更多訊息阻塞時的介面。當訊息佇列為空,等待更多訊息時,執行這個queueIdle函式,如果返回真,繼續保持你的idlehandler的活躍狀態,如果返回值為false,那麼就會從應用程式中移除這個IdleHandler,否則的話就會在應用程式中繼續維護著這個IdleHandler,下次空閒時仍會再執會這個IdleHandler。但是如果訊息佇列裡面有訊息,但是他們的執行時間在當前時間之後,queueIdle也會被呼叫。
再往下看程式碼:
/**
     * Add a new {@link IdleHandler} to this message queue.  This may be
     * removed automatically for you by returning false from
     * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
     * invoked, or explicitly removing it with {@link #removeIdleHandler}.
     * 
     * <p>This method is safe to call from any thread.
     * 
     * @param handler The IdleHandler to be added.
     */
    public final void addIdleHandler(IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }

    向訊息佇列中新增一個新的MessageQueue.IdleHandler。當呼叫IdleHandler.queueIdle()返回false時,此MessageQueue.IdleHandler會自動的從訊息佇列中移除。或者呼叫removeIdleHandler(MessageQueue.IdleHandler)也可以從訊息佇列中移除MessageQueue.IdleHandler
這個方法是執行緒安全的。

有新增,就有刪除:
/**
     * Remove an {@link IdleHandler} from the queue that was previously added
     * with {@link #addIdleHandler}.  If the given object is not currently
     * in the idle list, nothing is done.
     * 
     * @param handler The IdleHandler to be removed.
     */
    public final void removeIdleHandler(IdleHandler handler) {
        synchronized (this) {
            mIdleHandlers.remove(handler);
        }
    }

    從佇列中移除之前呼叫addIdlehandler(MessageQueue.IdleHandler)新增的MessageQueue.IdleHandler。如果handler不在當前的空閒列表,不做任何事。
看一下它的構造方法:
MessageQueue() {
        nativeInit();
    }

呼叫native程式碼,完成初始化。
重寫了類的銷燬:
@Override
    protected void finalize() throws Throwable {
        try {
            nativeDestroy();
        } finally {
            super.finalize();
        }
    }

也是呼叫native程式碼,完成類的銷燬。
下面就是獲取下一個Message方法:
final Message next() {
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;

        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            nativePollOnce(mPtr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                final Message msg = mMessages;
                if (msg != null) {
                    final long when = msg.when;
                    if (now >= when) {
                        mBlocked = false;
                        mMessages = msg.next;
                        msg.next = null;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    } else {
                        nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
                    }
                } else {
                    nextPollTimeoutMillis = -1;
                }

                // If first time, then get the number of idlers to run.
                if (pendingIdleHandlerCount < 0) {
                    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("MessageQueue", "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;
        }
    }
它在取Message來進行處理時通過判斷MessageQueue裡面的Message是否符合時間要求來決定是否需要把Message取出來做處理,通過這種方式做到訊息的定時處理。所以呼叫這個函式的時候,有可能會讓執行緒進入等待狀態。什麼情況下,執行緒會進入等待狀態呢?兩種情況,一是當訊息佇列中沒有訊息時,它會使執行緒進入等待狀態;二是訊息佇列中有訊息,但是訊息指定了執行的時間,而現在還沒有到這個時間,執行緒也會進入等待狀態。訊息佇列中的訊息是按時間先後來排序的。
先看一下兩個引數的意思:pendingIdleHandlerCount:空閒的handler個數。只有在第一次迴圈的時候值為-1。nextPollTimeoutMillis:字面理解下次輪詢時間,如果當前訊息佇列中沒有訊息,它要等待的時候,for迴圈開始時,傳入的值為0,表示不等待。
在for迴圈內部
 if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

如果nextPollTimeoutMillis值不為0,則等待訊息。然後執行下面語句是看看當前訊息佇列中有沒有訊息:
 nativePollOnce(mPtr, nextPollTimeoutMillis);

nativePollOnce返回後,next函式將從mMessages中提取一個訊息。也就是說,要讓nativePollOnce返回,至少要新增一個訊息到訊息佇列,否則nativePollOnce不過是做了一次無用功罷了。
如果nativePollOnce在Native層等待,就表明Native層也可以投遞Message(訊息,為了適應業界的習慣本書沿用英文,必要時才用中文,其他詞同此),但是從Message類的實現程式碼上看,該類和Native層沒有建立任何關係(即Native層不太可能去構造Java層的Message物件並把它插入到Java層的Message佇列中)。那麼nativePollOnce在等待什麼呢?
 對於上面的問題,相信有些讀者心中已有了答案:nativePollOnce除了等待Java層來的Message,還在Native層做了大量的工作。
然後再看synchronized內部程式碼:
synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                final Message msg = mMessages;
                if (msg != null) {
                    final long when = msg.when;
                    if (now >= when) {
                        mBlocked = false;
                        mMessages = msg.next;
                        msg.next = null;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    } else {
                        nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
                    }
                } else {
                    nextPollTimeoutMillis = -1;
                }

                // If first time, then get the number of idlers to run.
                if (pendingIdleHandlerCount < 0) {
                    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);
            }


先嚐試檢索出下一個message,如果存在就返回。如果message為空,nextPollTimeoutMillis就賦值為-1.就必須等待下一次訊息。如果不為空,就判斷時間是否可以處理這個訊息,如果符合條件,把訊息傳給looper處理。否則,算出需要等待時間,等待到該時間,然後執行
// If first time, then get the number of idlers to run.
                if (pendingIdleHandlerCount < 0) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount == 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

如果是第一次,得到閒置的idler handler去執行。如果沒有閒置的idlehandler,阻塞,然後繼續迴圈等待更多訊息。

if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
把註冊在mIdleHandlers中的IdleHandler取出來,放在mPendingIdleHandlers陣列中去。
    
    接下來就是執行這些註冊了的IdleHanlder了:
// 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("MessageQueue", "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

執行空閒處理器。我們只能達到這個程式碼塊在第一次迭代。執行完這些IdleHandler之後,執行緒下次呼叫nativePollOnce函式時,就不設定超時時間了,因為,很有可能在執行IdleHandler的時候,已經有新的訊息加入到訊息佇列中去了,因此,要重置nextPollTimeoutMillis的值:
 // 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;

再接下來就是插入操作:
final boolean enqueueMessage(Message msg, long when) {
        if (msg.isInUse()) {
            throw new AndroidRuntimeException(msg
                    + " This message is already in use.");
        }
        if (msg.target == null && !mQuitAllowed) {
            throw new RuntimeException("Main thread not allowed to quit");
        }
        final boolean needWake;
        synchronized (this) {
            if (mQuiting) {
                RuntimeException e = new RuntimeException(
                    msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            } else if (msg.target == null) {
                mQuiting = true;
            }

            msg.when = when;
            //Log.d("MessageQueue", "Enqueing: " + msg);
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked; // new head, might need to wake up
            } else {
                Message prev = null;
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
                msg.next = prev.next;
                prev.next = msg;
                needWake = false; // still waiting on head, no need to wake up
            }
        }
        if (needWake) {
            nativeWake(mPtr);
        }
        return true;
    }

if (msg.isInUse()) {
            throw new AndroidRuntimeException(msg
                    + " This message is already in use.");
        }
        if (msg.target == null && !mQuitAllowed) {
            throw new RuntimeException("Main thread not allowed to quit");
        }
     如果msg正在被使用,或者msg對應的handler為空或者不允許退出,均報異常。
if (mQuiting) {
                RuntimeException e = new RuntimeException(
                    msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            } else if (msg.target == null) {
                mQuiting = true;
            }

     是否正在退出,如果退出,返回false,如果msg對應的handler為空,把正在退出設為true。
Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked; // new head, might need to wake up
            } else {
                Message prev = null;
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
                msg.next = prev.next;
                prev.next = msg;
                needWake = false; // still waiting on head, no need to wake up
            }

如果p為空,或者when=0或者插入時間小於p.when時,就會把傳入的訊息插入到佇列頭(這也就解釋了上篇文章中提到的問題,when=0時,插入佇列頭部),否則,插入隊尾。
接下來是刪除。
final boolean removeMessages(Handler h, int what, Object object,
            boolean doRemove) {
        synchronized (this) {
            Message p = mMessages;
            boolean found = false;

            // Remove all messages at front.
            while (p != null && p.target == h && p.what == what
                   && (object == null || p.obj == object)) {
                if (!doRemove) return true;
                found = true;
                Message n = p.next;
                mMessages = n;
                p.recycle();
                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)) {
                        if (!doRemove) return true;
                        found = true;
                        Message nn = n.next;
                        n.recycle();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
            
            return found;
        }
    }

    final void removeMessages(Handler h, Runnable r, Object object) {
        if (r == null) {
            return;
        }

        synchronized (this) {
            Message p = mMessages;

            // Remove all messages at front.
            while (p != null && p.target == h && p.callback == r
                   && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycle();
                p = n;
            }

            // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && n.callback == r
                        && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycle();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }
最後一個刪除:
final void removeCallbacksAndMessages(Handler h, Object object) {
        synchronized (this) {
            Message p = mMessages;

            // Remove all messages at front.
            while (p != null && p.target == h
                    && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycle();
                p = n;
            }

            // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycle();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

      
檢視更多原始碼內容:Android原始碼解析                           

                                                      

相關文章