Android 異步消息處理機制前篇(二):深入理解Message消息池
版權聲明:本文出自汪磊的博客,轉載請務必註明出處。
上一篇中共同探討了ThreadLocal,這篇我們一起看下常提到的Message消息池到底是怎麽回事,廢話少說吧,進入正題。
對於稍有經驗的開發人員來說我們在使用Handler發送異步消息獲取Message的時候都會使用如下代碼獲取一個Message對象:
1 Message msg = mHandler.obtainMessage();
而不是直接new一個:
1 Message msg = new Message();
二者的主要區別就是上面的用到緩存池概念,如果池中有閑著的則拿來用,沒有則new一個Message。後者則沒有這個機制,直接new一個拿來用。
接下來我們分析一下這個緩存池是怎麽實現的。
Message緩存池源碼分析
Handler中obtainMessage()方法實質還是調用的Message中obtain()方法,這裏就直接看Message中obtain()方法源碼了:
1 public static Message obtain() { 2 synchronized (sPoolSync) { 3 if (sPool != null) { 4 Message m = sPool; 5 sPool = m.next;6 m.next = null; 7 m.flags = 0; // clear in-use flag 8 sPoolSize--; 9 return m; 10 } 11 } 12 return new Message(); 13 }
第3行首先判斷sPool是否為null,如果為null則直接執行12行直接new一個Message返回,整個方法結束,sPool是什麽鬼?定義如下:
1 private staticMessage sPool;
看到了吧,就是一個Message對象,sPool其實就相當於一個頭指針,指向緩存池中第一個緩存的Message,分析完所有就會自然明白了其作用。
繼續向下分析。
sPool不為null則進入4-9行代碼邏輯,sPool不為null說明緩存池中存在空閑的Message.
第4行記錄sPool,並且第9行返回m作為整個方法的返回值,也就是返回緩存池中的空閑Message供外部使用,不需要額外內存開銷。
第5行sPool指向下一個緩存對象。
第6行m.next置為null,到這裏最重要的邏輯就完了,也許你還蒙蔽呢,這是什麽啊,其實很簡單Message的緩存池其實就是用了一個數據結構-單向鏈表。
接下來又要展示我強大的畫圖能力了,沒有什麽是一個圖示不能解決的:
假設此時緩存池中有三個空閑message:message1,message2,message3。sPool一開始指向頭部message1。
執行第4行代碼相當於圖中步驟①,沒什麽好解釋的。
執行第5行代碼相當於圖中步驟②,sPool指向下一個緩存message,此處為message2.
執行第6行代碼相當於圖中步驟③,message1與message2斷開連接。
怎麽樣這樣解釋該明白了,其實本身就很簡單。
7,8行就是清除標記以及改變sPoolSize大小,sPoolSize用來記錄緩存池中存在的元素個數,緩存池大小是有限制的,超過規定大小則不能再往裏面添加。
obtain()總結
好了,到此主要邏輯就分析完了,obtain()主要邏輯就是先判斷緩存池中是否存在空閑message,如果存在則返回頭部message,並且指針指向下一個空閑message,然後頭部的message與之後鏈表 斷開連接。如果不存在空閑message則直接new一個直接返回。
上面的邏輯都是從緩存池中獲取的操作,那什麽時候向緩存池中存放呢?我們繼續向下分析。
Message類中recycle()方法是用於回收用完的mesage,將此message會收到緩存池中,是這樣的嗎?我們看下源碼就知道了:
1 public void recycle() { 2 if (isInUse()) { 3 if (gCheckRecycle) { 4 throw new IllegalStateException("This message cannot be recycled because it " 5 + "is still in use."); 6 } 7 return; 8 } 9 recycleUnchecked(); 10 }
recycle方法中主要判斷當前message是否正在使用中,如果正在使用則拋出異常,沒被使用則調用recycleUnchecked()方法,接下來看下recycleUnchecked():
1 void recycleUnchecked() { 2 // Mark the message as in use while it remains in the recycled object pool. 3 // Clear out all other details. 4 flags = FLAG_IN_USE; 5 what = 0; 6 arg1 = 0; 7 arg2 = 0; 8 obj = null; 9 replyTo = null; 10 sendingUid = -1; 11 when = 0; 12 target = null; 13 callback = null; 14 data = null; 15 16 synchronized (sPoolSync) { 17 if (sPoolSize < MAX_POOL_SIZE) { 18 next = sPool; 19 sPool = this; 20 sPoolSize++; 21 } 22 } 23 }
4-14主要就是清除一些當前標記。
17行,MAX_POOL_SIZE就是規定的緩存池中最多緩存message的個數,如果當前已經存儲的數量小於規定的最大緩存個數則繼續向下執行。
18,19行就是重點了,又到展示我強大畫圖能力的時候了,一張圖解決:
比如緩存池中鏈表中為message2,message3,sPool指向頭部message2。
此時,message1被回收執行recycle()操作。最終執行到recycleUnchecked()的18,19行邏輯。
18行:相當於將圖中message1的next指針指向sPool,此時sPool指向message2,也就是將message1與message2鏈接,也就是圖中①操作。
19行:sPool重新定位到當前被回收的message,這裏也就是message1。相當於圖中②操作
recycle()總結
好了,到這裏回收就講完了,最主要就是18,19行邏輯,recycle()最主要就是將當前message放入緩存池鏈表頭部。
到此,我想講解的就完了,本篇核心就是數據結構中單項鏈表的實際應用,如果單向鏈表你很熟悉,我覺得這裏應該很輕松的就理解了,即使不是很熟悉,用心思考一下也應該能理解,這裏不難理解。
本篇就到此為止了,下一篇Android異步消息處理機制完全解析。
Android 異步消息處理機制前篇(二):深入理解Message消息池