1. 程式人生 > >Android 異步消息處理機制前篇(二):深入理解Message消息池

Android 異步消息處理機制前篇(二):深入理解Message消息池

連接 guid ply 指針 cau ann 區別 就會 消息處理機制

版權聲明:本文出自汪磊的博客,轉載請務必註明出處。

上一篇中共同探討了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 static
Message 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消息池