Android源碼分析之Message

  準備開始寫點東西,算是對本身閱讀源碼的一個記錄/筆記,也但願能對一樣感興趣的人有所幫助,但願能堅持下去,加油。java

  在Android的開發中,咱們常常用到Handler.postXXX方法,或者View.postXXX方法,用來在下一次looper到來時執行。android

我是那樣的人,什麼事情最好可以知道下內部實現機理是什麼,不然我在用它的時候可能會以爲不爽,或者說不天然,不太願意去用。異步

典型例子就是我始終不太願意用Android引入的SparseArray<E>,而是一直堅持Java的HashMap<Key, Value>,直到我本身讀了async

SparseArray<E>的源碼,纔開始放心大膽的使用(後面會寫一篇文章專門分析它)。ide

  首先來看下面的代碼:oop

private Runnable mRunnable = new Runnable() {
    @Override
    public void run() {
        // do something...
        removeCallbacks(this);
    }
};

postDelayed(mRunnable, SOME_DELAY_IN_MILLIMS);

第一直覺告訴我run方法裏的removeCallbacks(this);調用顯然是多餘的。通讀代碼發現的確是如此,由於任何Messagepost

(即便post的是Runnable也會被包裝到Message裏)在被處理以前都已經從MessageQueue裏取出來了(delete掉了,因此客戶端測試

代碼大可沒必要有這樣的代碼)。這裏順便提下慎用View.removeCallbacks的返回值,看源碼:ui

 1 /**
 2      * <p>Removes the specified Runnable from the message queue.</p>
 3      *
 4      * @param action The Runnable to remove from the message handling queue
 5      *
 6      * @return true if this view could ask the Handler to remove the Runnable,
 7      *         false otherwise. When the returned value is true, the Runnable
 8      *         may or may not have been actually removed from the message queue
 9      *         (for instance, if the Runnable was not in the queue already.)
10      *
11      * @see #post
12      * @see #postDelayed
13      * @see #postOnAnimation
14      * @see #postOnAnimationDelayed
15      */
16     public boolean removeCallbacks(Runnable action) {
17         if (action != null) {
18             final AttachInfo attachInfo = mAttachInfo;
19             if (attachInfo != null) {
20                 attachInfo.mHandler.removeCallbacks(action);
21                 attachInfo.mViewRootImpl.mChoreographer.removeCallbacks(
22                         Choreographer.CALLBACK_ANIMATION, action, null);
23             } else {
24                 // Assume that post will succeed later
25                 ViewRootImpl.getRunQueue().removeCallbacks(action);
26             }
27         }
28         return true;
29     }

咱們能夠看到這個方法always返回true,因此不要基於它的返回值作任何事情,還有它的返回值的意義也須要格外留意下。this

我在第一次看到這個方法時就自覺得然的以爲返回值確定表明了Runnable action有沒有成功地從MessageQueue中移除,true表明成功

移除了,false表明移除失敗,呵呵,你想錯了。仔細看看方法的doc,人家說的是return true表示這個view可讓它的Handler

去處理這件事情,並沒說起處理的結果,並且即便返回true的時候也不能說明Runnable就已經從MessageQueue中移除了,

好比說此時Runnable已經不在MessageQueue中了;其餘狀況都是返回false。這裏順便看眼View.postDelayed方法:

1 public boolean postDelayed(Runnable action, long delayMillis) {
2         final AttachInfo attachInfo = mAttachInfo;
3         if (attachInfo != null) {
4             return attachInfo.mHandler.postDelayed(action, delayMillis);
5         }
6         // Assume that post will succeed later
7         ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
8         return true;
9     }

這裏不管是postDelayed仍是removeCallbacks方法都首先檢查了本身的mAttachInfo,若是非空才delegate給attachInfo的Handler

處理,因此你儘可能不要過早(mAttachInfo還沒初始化完畢)的調用這些方法。好比在早期版本的Android中若是你過早的調用post,

runnable不會被執行,參考這個問題:

http://stackoverflow.com/questions/4083787/runnable-is-posted-successfully-but-not-run;

感興趣的同窗能夠在google中搜索view post runnable not run或者自行驗證。我如今分析的是Android4.4的源碼,依如今的代碼來看

即便mAttachInfo是null,也會執行ViewRootImpl.getRunQueue().postDelayed(action, delayMillis); 因此可能在較新的平臺上不是

問題(有待考證)。經測試驗證確實沒問題,即便在Activity.onCreate中調用View.postXXX方法,runnable仍是會被執行。

  好了說了一大堆了,開始正題。做爲開始我今天挑了一個最簡單的開始分析,那就是Message.java文件。

  其實說白了,Message就是一個數據類,持有data的。基本的數據字段我就不介紹了,都能望文生義。看下幾個我以爲有必要的字段:

 1  /*package*/ Handler target;
 2 
 3     /*package*/ Runnable callback;
 4 
 5     // sometimes we store linked lists of these things
 6     /*package*/ Message next;
 7 
 8     private static final Object sPoolSync = new Object();
 9     private static Message sPool;
10     private static int sPoolSize = 0;
11 
12     private static final int MAX_POOL_SIZE = 50;

target是消息的處理者,在之後Looper.loop()方法中Message被從MessageQueue取出來後會調用msg.target.dispatchMessage(msg);

callback是消息要執行的動做action。這裏提早插播下Handler的dispatchMessage方法:

 1 public void dispatchMessage(Message msg) {
 2         if (msg.callback != null) {
 3             handleCallback(msg);
 4         } else {
 5             if (mCallback != null) {
 6                 if (mCallback.handleMessage(msg)) {
 7                     return;
 8                 }
 9             }
10             handleMessage(msg);
11         }
12     }

咱們能夠看到Handler在分發消息的時候,Message自身的callback優先級高,先被調用若是非空的話(callback的run方法直接被調用)。

next表示消息隊列中的下一個Message,相似單鏈表的概念。

剩下的pool相關的字段都是Message引入的重用(reuse)所要用到的變量,sPoolSync是對象鎖,由於Message.obtain方法會在任意

線程調用;sPool表明接下來要被重用的Message對象;sPoolSize表示有多少個能夠被重用的對象;MAX_POOL_SIZE顯然是pool的上限,

這裏hardcode是50。這裏我要分析的就2個方法,

obtain和recycle,代碼以下:

 1 /**
 2      * Return a new Message instance from the global pool. Allows us to
 3      * avoid allocating new objects in many cases.
 4      */
 5     public static Message obtain() {
 6         synchronized (sPoolSync) {
 7             if (sPool != null) {
 8                 Message m = sPool;
 9                 sPool = m.next;
10                 m.next = null;
11                 sPoolSize--;
12                 return m;
13             }
14         }
15         return new Message();
16     }
17 
18 /**
19      * Return a Message instance to the global pool.  You MUST NOT touch
20      * the Message after calling this function -- it has effectively been
21      * freed.
22      */
23     public void recycle() {
24         clearForRecycle();
25 
26         synchronized (sPoolSync) {
27             if (sPoolSize < MAX_POOL_SIZE) {
28                 next = sPool;
29                 sPool = this;
30                 sPoolSize++;
31             }
32         }
33     }

首先咱們來看obtain方法,第一次調用也就是說沒有什麼東西能夠重用,這時sPool是null,直接new一個Message對象返回,等到

Message對象使用完畢(在Looper.loop方法最後有msg.recycle();這樣的代碼),它的recycle會被調用,在recycle裏首先會調用

clearForRecycle方法,它只是把各個字段置空或清零。接下來sPoolSize沒到上限,next保存下sPool的舊值(也就是在當前Message

回收利用以前上一個要被回收利用的對象),而後sPool被更新成新值,即當前Message,sPoolSize加1,表示又多了一個能夠重用的

Message對象。以後在等到obtain被調用的時候就不是直接return一個new Message了,由於咱們已經有能夠重用的Message對象了。

將sPool的值設置給咱們要返回的Message m對象,接着sPool被更新成上一個要被重用的Message對象(相比recycle是反向過程),

最後設置m的next字段爲空(m.next會在從新入隊列的時候被設置成合適的值),相應的sPoolSize減1,表示可重用的對象少了一個,

最後返回重用的對象m。

  基於有這麼個回收再利用的機制,Android建議咱們調用Message的obtain方法來獲取一個Message對象,而不是調用ctor,由於很

可能會省掉分配一個新對象的開銷。

  到目前爲止,咱們好像忽略了Message的一個(重要)方面,即異步性,代碼以下:

  /**
     * Sets whether the message is asynchronous.
     *
     * Asynchronous messages represent interrupts or events that do not require global ordering
     * with represent to synchronous messages.  Asynchronous messages are not subject to
     * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
     *
     * @param async True if the message is asynchronous.
     *
     * @see #isAsynchronous()
     * @see MessageQueue#enqueueSyncBarrier(long)
     * @see MessageQueue#removeSyncBarrier(int)
     *
     * @hide
     */
    public void setAsynchronous(boolean async) {
        if (async) {
            flags |= FLAG_ASYNCHRONOUS;
        } else {
            flags &= ~FLAG_ASYNCHRONOUS;
        }
    }

看方法的doc,咱們知道異步的Message表示那些不須要全局順序的中斷或事件(相比同步Message來講),這裏的全局順序是指

MessageQueue中的前後順序,並且異步消息的處理不受MessageQueue中引入的enqueueSyncBarrier(long)方法的影響;

具體見MessageQueue的next()方法中有段這樣的代碼:

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

也就是說當咱們在隊列中遇到一個sync barrier的時候,緊接着的同步Message的處理就會被本次循環忽略,而是直奔下一個

異步的消息。sync barrier是經過MessageQueue中的enqueueSyncBarrier(long when)、removeSyncBarrier(int token)

調用來實現添加、刪除的;它們是在android.view.ViewRootImpl執行Traversals相關的代碼時被調到的,代碼以下:

    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().postSyncBarrier(); // 添加一個sync barrier
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            scheduleConsumeBatchedInput();
        }
    }

    void unscheduleTraversals() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); // 移除對應的sync barrier
            mChoreographer.removeCallbacks(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }

    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); // 移除對應的sync barrier

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }

            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals");
            try {
                performTraversals();
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

  Message類的分析就到這了,之後會陸續分析下常見於Android開發中的類。。。(因爲本人水平有限,歡迎批評指正)

相關文章
相關標籤/搜索