Message.obtain()中的單鏈表棧快取
Message.obtain()中的單鏈表棧快取
Android中的Message.java用單鏈表實現了一個size=50的棧,用作快取。以下結合原始碼和圖分析存取過程。
存
void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = -1; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }
呼叫時機:在Looper.loop中呼叫,具體地說在Handler.handleMessage()之後立刻呼叫。
public void static loop(){ final Looper me=Looper.myLopper(); final MessageQueue queue = me.mQueue; for(;;){ //... Message msg = queue.next(); msg.target.dispatchMessage(msg); //上面的dispatchMessage最終執行到handler的handleMessasge了 msg.recycleUnchecked(); } }
取
public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); }
呼叫時機:這個應該由開發者在需要Message物件的時候手動呼叫來獲取快取池中的Message物件。
結合圖來分析存取過程中單鏈表棧的變化
第一次存取
- 初始狀態,sPool指向null。沒有任何Messsage物件。

1547627128889.png
-
呼叫一次Message.obtain()。由於sPool==null,因此直接new Message()並返回給開發者使用,在用完後,Looper為其呼叫了 msg.recycleUnchecked();將其回收。
synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool;//1 sPool = this;//2 sPoolSize++;//3 } }
開始分析,此時還未執行程式碼段1,2,3。

1547627414730.png
執行1。(此時Message1.next實際上指向了Null)

執行2。(sPool引用指向了物件Message1)

執行3。將快取池數量標記+1。
第二次存取
- 初始狀態

-
開發者呼叫Messsage.obatin()來獲取Message物件
public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool;//1 sPool = m.next;//2 m.next = null;//3 m.flags = 0; // clear in-use flag sPoolSize--; return m;//4 } } return new Message(); }
執行1。(建立一個Message m引用,指向了sPool所指向的物件Message1)
1547628442298.png
執行2。(讓sPool指向null)

1547628468565.png
執行3。還是上面的原圖
執行4。將引用m返回,交給開發者,其指向了物件Message1,開發者可以拿著這個引用對其做處理。
- 由Looper來呼叫msg.recycleUnchecked將其回收。其所有過程和第一次存取的回收過程如出一轍。
多次存取
- 一次性呼叫多次obtain,由於當快取池中沒有Message物件時,sPool指標就會==null,因此就會直接生成Message例項,如圖:

1547628887922.png
-
此時每一個Message都被handler處理完成,並準備一個一個被Looper回收。
執行Message1的回收:

1547628987637.png
執行Message2的回收:

1547629051003.png
執行Message3的回收

1547629167436.png
執行Message4的回收

1547629182292.png
如圖,此時Message維護了一個單鏈表構成的棧,sPool指向棧頂
當obtainMessage()時,建立一個引用指向sPool,即指向棧頂的Message4,將sPool指向Message4.next即Message3,再將Message4.next=null,將Message4物件脫離單鏈!
表,最後將引用返回給開發者,供開發者操縱物件Message4。圖不再給出。