準備開始寫點東西,算是對本身閱讀源碼的一個記錄/筆記,也但願能對一樣感興趣的人有所幫助,但願能堅持下去,加油。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開發中的類。。。(因爲本人水平有限,歡迎批評指正)