準備開始寫點東西,算是對自己閱讀源碼的一個記錄/筆記,也希望能對同樣感興趣的人有所協助,希望
能堅持下去,嘿嘿。
在Android的開發中,我們經常用到Handler.postXXX方法,或者View.postXXX方法,用來在下一次looper
到來時執行。我是那樣的人,什麼事情最好能夠知道下內部實現機理是什麼,否則我在用它的時候可能會覺得不爽,
或者說不自然,不太願意去用。典型例子就是我始終不太願意用Android引入的SparseArray<E>,而是一直堅持
Java的HashMap<Key, Value>,直到我自己讀了SparseArray<E>的源碼,才開始放心大膽的使用。
其實說來也巧,為什麼忽然決定寫這個,是有一天看到了類似這樣的代碼:
Runnable mRunnable = removeCallbacks(
第一直覺告訴我run方法裡的removeCallbacks(this);調用顯然是多餘的。通過通讀代碼發現的確是如此,因為任何Message
(即使post的是Runnable也會被封裝到Message裡)在被處理之前都已經從MessageQueue裡取出來了(delete掉了,所以用戶端
代碼大可不必有這樣的代碼)。這裡順便提下慎用View.removeCallbacks的傳回值,看源碼:
(action != AttachInfo attachInfo = (attachInfo != Choreographer.CALLBACK_ANIMATION, action, } }
我們可以看到這個方法always返回true,所以不要基於它的傳回值做任何事情,還有它的傳回值的意義也需要格外留意下。
我在第一次看到這個方法時就自以為然的覺得傳回值肯定代表了Runnable action有沒有成功地從MessageQueue中移除,true代表成功
移除了,false代表移除失敗,呵呵,你想錯了。仔細看看方法的doc,人家說的是return true表示這個view可以讓它的Handler
去處理這件事情,並沒提及處理的結果,而且即使返回true的時候也不能說明Runnable就已經從MessageQueue中移除了,
比如說此時Runnable已經不在MessageQueue中了;其他情況都是返回false。這裡順便看眼View.postDelayed方法:
postDelayed(Runnable action, AttachInfo attachInfo = (attachInfo != }
這裡不論是postDelayed還是removeCallbacks方法都首先檢查了自己的mAttachInfo,如果非空才delegate給attachInfo的Handler處理,所以
你盡量不要過早(mAttachInfo還沒初始化完畢)的調用這些方法,在早些版本的Android中runnable不會被執行,參考這個問題:
http://stackoverflow.com/questions/4083787/runnable-is-posted-successfully-but-not-run
我現在分析的是Android4.4的源碼,具體是怎樣,我現在家裡還沒法驗證,等周一到公司了驗證下,然後在update下,當然感興趣的同學可以在google中搜尋view post runnable not run或者自行驗證。不過依現在的代碼來看即使mAttachInfo是null,也會執行ViewRootImpl.getRunQueue().postDelayed(action, delayMillis); 所以可能在較新的平台上不是問題(有待考證)。目前我還沒來得及分析它的源碼(之後會寫的)。
好了說了一大堆了,開始正題。作為開始我今天挑了一個最簡單的開始分析,嘿嘿,那就是Message.java檔案。
其實說白了,Message就是一個資料類,持有data的。基本的資料欄位我就不介紹了,都能望文生義。看下幾個我覺得有必要的欄位:
Object sPoolSync = sPoolSize = 0 MAX_POOL_SIZE = 50;
target是訊息的處理者,在以後Looper.loop()方法中Message被從MessageQueue取出來後會調用msg.target.dispatchMessage(msg);
callback是訊息要執行的動作action。這裡提前插播下Handler的dispatchMessage方法:
(msg.callback != } (mCallback != }
我們可以看到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,代碼如下:
(sPool != Message m = sPool = m.next = sPoolSize-- (sPoolSize < next = sPool = sPoolSize++ }
首先我們來看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類的分析就到這了,以後會陸續分析下常見於Android開發中的類。。。(由於本人水平有限,歡迎批評指正)