Android Handler機制(二)---MessageQueue源碼解析


MessageQueue

1.變量

    private final boolean mQuitAllowed;//表示MessageQueue是否允許退出
    @SuppressWarnings("unused")
    private long mPtr; //mPtr是native代碼相關的
    
    Message mMessages; //表示消息隊列的頭Head
    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
    private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
    private IdleHandler[] mPendingIdleHandlers;
    private boolean mQuitting;

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

    // The next barrier token.
    // Barriers are indicated by messages with a null target whose arg1 field carries the token.
    private int mNextBarrierToken; 

mQuitAllowed表示MessageQueue是否允許退出,系統創建的UI線程的MessageQueue是不允許的,其他客戶端代碼創建的都是允許的;

mPtr是native代碼相關的,指向C/C++代碼中的某些對象(指針),其他一些nativeXXX()相關的函數本文暫不做分析;

mMessages表示消息隊列的頭Head;

mIdleHandlers是IdldHandler接口的ArrayList, mPendingIdleHandlers是數組版本,在后面的代碼中會將ArrayList的內容拷貝到它里面;

mQuitting表示當前隊列是否處於正在退出狀態;

mBlocked表示next()調用是否被block在timeout不為0的pollOnce上;

mNextBarrierToken表示下一個barrier token,barrier用target==null, arg1==token的Message對象表示;

IdleHandler callback接口:

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();
    }
public void addIdleHandler(IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }
 public void removeIdleHandler(IdleHandler handler) {
        synchronized (this) {
            mIdleHandlers.remove(handler);
        } 

IdleHandler接口表示當MessageQueue發現當前沒有更多消息可以處理的時候則順便干點別的事情的callback函數(即如果發現idle了,

那就找點別的事干)。callback函數有個boolean的返回值,表示是否keep。如果返回false,則它會在調用完畢之后從mIdleHandlers

中移除。這里讓我們來看一個具體的例子(實現),ActivityThread.java里的一個內部類,代碼如下:

final class GcIdler implements MessageQueue.IdleHandler {
        @Override
        public final boolean queueIdle() {
            doGcIfNeeded();
            return false;
        }
    } 

這是一個gc相關的IdleHandler,即如果沒有更多的消息可以處理就會抽空doGcIfNeeded(),最后返回false表示不保留在mIdleHandlers

中,即用一次就扔了,只執行一遍。

MQ的構造方法:在Looper中就是調用的這個構造方法。

MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;  //true是允許推出
        mPtr = nativeInit(); //這是一個本地方法
    }

MQ的構造方法簡單的調用了nativeInit()來進行初始化,這是一個jni方法,也就是說,可能是在JNI層維持了它這個消息隊列的對象。在message中有好多native方法,可以看出message是比較底層的一個類。

接下來看MessageQueue的核心方法,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();
            }

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

首先初始化2個接下來要用到的變量,緊接着進入無限for循環中,其某次循環主要做這么幾件事情:

1. 如果nextPollTimeoutMillis != 0的話,調用Binder.flushPendingCommands();

2. 調用nativePollOnce(mPtr, nextPollTimeoutMillis);

3. 進入一個大的同步塊,嘗試獲取一個可以處理的消息,具體做法是,記錄當前時間now,初始化變量prevMsg為null,msg為mMessges;

如果msg是一個sync barrier消息,則直奔下一個asynchronous消息(這之間的所有同步消息會被本次循環忽略,也就是說遇到這種情況,

next方法會從找到的異步消息的位置開始嘗試獲取一個可以處理的消息並返回),同時更新prevMsg,msg的值;

4.當退出此do...while循環的時候msg可能為空(走到隊列尾了),或者成功找到了一個這樣的(異步)消息。

5.如果是到隊尾了即msg==null,則表示沒更多的消息了,設置nextPollTimeoutMillis = -1;否則當now<msg.when(msg的時間還沒到),設置一個合理的等待時間,即調用

nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);

6.當msg到了該處理的時間了,也就是說我們找到了這樣一個消息可以返回了,設置mBlocked為false,將msg從mMessages隊列中取出來(類似單鏈表的刪除操作),並執行

msg.next=null、msg.markInUse(),返回msg。

7.如果到這一步了還沒return的話,那說明還沒有可以處理的消息,檢查下隊列是否要求退出了,如果是執行dispose(),返回null。當Looper的loop方法看到null的message的時候會退出loop。

8.接下來既然沒消息可以處理,那就該處理IdleHandler了。如果pendingIdleHandlerCount小於0(注意其在第一次進入for循環是被初始化為-1)且沒更多的消息需要處理,設置pendingIdleHandlerCount=mIdleHandlers.size();

9.如果pendingIdleHandlerCount還是<=0的話,表示沒有idle handler需要執行,

設置mBlocked為true,接着進入下次循環。

10.接下來就是根據mIdleHandlers來初始化mPendingIdleHandlers。退出同步塊后我們就剩下最后一件事了,那就是run Idle handlers。一個for循環用來做這就事情,在循環內如果IdleHandler沒必要保留,則會從mIdleHandlers中移除。

11. 最后重置pendingIdleHandlerCount為0(也就是4只會在第一次循環的時候執行一次),將nextPollTimeoutMillis設為0,因為當我們在

執行4的時候,新的Message可能已經到來了,所以我們需要立即開始(不需要等待)下次循環來檢查。

**上面很長,簡單點說就是:next方法取出下一個Message(從頭部取),如果沒有Message可以處理,就可以處理下IdleHandler。

看完了next()方法,接下來我們來看enqueue()方法:

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

在后邊介紹Handler的時候,可以看到,許多跟message(包括runnable)相關的操作,最終都delegate給了MessageQueue的enqueue方法。

和任何方法一樣,在enqueue之前,也都是對參數msg的檢查,比如msg如果在使用中,或者msg的target是null,都會拋出AndroidRuntimeException,進行完條件檢查后,會進入真正的處理邏輯。接下來的操作類似在一張單鏈表中插入一個元素:進入同步塊

1. 如果此隊列處於正在退出的狀態則不能在往里入隊了,不能插入元素了,在這種情況下會拋出RuntimeException,然后return false,

表示失敗了;

2. 接下來表示隊列的狀態ok,設置msg的when字段,臨時變量p指向隊列頭;(必要的初始化,准備工作)

3. 如果隊列是空的或when==0或when<p.when,也就是說要插入的這個message應該在第一個位置也就是隊首,那么它將是新的Head,將它和原先的隊列連接起來;

4. 否則插入將發生在隊列中間的某個位置(有可能是隊尾),將msg插在第一個p的前面,p滿足這個條件(p == null || when < p.when)。

最后退出同步塊,返回true,表示操作(入隊)成功。

**messageQueue中的元素是按序按時間先后插入的(先執行的在前)。

removeMessages():

接着我們來看3個類似的removeMessages()方法,

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

    void removeMessages(Handler h, Runnable r, Object object) {
        if (h == null || 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.recycleUnchecked();
                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.recycleUnchecked();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

    void removeCallbacksAndMessages(Handler h, Object object) {
        if (h == null) {
            return;
        }

        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.recycleUnchecked();
                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.recycleUnchecked();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    } 

這3個方法只是remove的條件不同,其主邏輯都是相同的,即從隊列中刪除所有匹配的元素。總體思想都是先從隊首刪除,如果刪除了則隊首

指向接下來的元素,重復這個過程,直到第一個不匹配的元素出現。接着從這個元素之后(after front)開始查找並刪除,

方法是鏈表刪除后一個節點的方法,即p.next=nn。注意這里都是刪除所有匹配的消息,而不是第一個匹配的。

最后看下隊列的quit()方法:

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

quit方法根據所傳參數safe的值,有2種不同的退出策略,如果是safe的退出,則執行removeAllFutureMessagesLocked(),

其內部的邏輯為如果隊首的元素還沒到期那說明隊列中其他所有的元素也都沒到期,所以等同於刪除所有的消息即調用

removeAllMessagesLocked();否則遍歷隊列找到第一個p.when>now這樣的message元素p,(更新隊列的尾部p.next=null,

縮短隊列)從p開始一直到隊列結束都是要被刪掉的元素,全部刪除之;如果是unsafe的退出,則所有message都直接被刪除並

回收即調用removeAllMessagesLocked()。

      Message是鏈表結構,MessageQueue中的好多操作都是基於鏈表,看起來困難的話,可以畫畫圖,就很清晰啦。。。

Message的源碼分析:http://www.cnblogs.com/jycboy/p/5786551.html

 

MessageQueue源碼解析:http://www.cnblogs.com/jycboy/p/5786682.html

 

Handler源碼解析:http://www.cnblogs.com/jycboy/p/5791457.html

 

Message源碼解析:http://www.cnblogs.com/jycboy/p/5786551.html

 

轉發注明出處: http://www.cnblogs.com/jycboy/p/5786682.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM