1. 程式人生 > >Android非同步訊息處理

Android非同步訊息處理

非同步訊息簡介 非同步訊息和執行緒的區別在於,執行緒執行完run()方法後,執行緒就結束了,而非同步訊息是線上程內部有一個訊息佇列,寫一個死迴圈, 一直去訊息佇列裡去取訊息,然後根據訊息型別處理相應的操作,如果取不到訊息就一直在等待。 非同步認為一般用於:任務需要常駐,比如處理使用者互動的螢幕觸控事件處理;根據不同的訊息型別處理不同的操作。 實現上就是: 1.每個非同步任務要有一個訊息佇列; 2使用while(true)無限迴圈,讀取訊息,處理訊息,執行回撥函式等; 3,外部可以向佇列發訊息,訊息佇列注意執行緒安全。 Android中的非同步訊息 看下圖,這個就是android中的實現圖, 可以這樣描述: (1)線上程內部有一個或者多個Handler物件,外部程式通過Handler物件向執行緒傳送非同步訊息, 訊息由Handler物件傳遞到MessageQueue物件中。 (2)執行緒的主執行程式中從MessageQueue中讀取訊息,並回調Handler物件中的回到函式handleMessage() 注意:MessageQueue線上程內部只能包含一個;每個訊息對應一個Handler物件
Looper,MessageQueue,Handler的原始碼介紹 先看下使用
----------------------------------------------------------- Looper.java Looper的作用有兩點: 第一個是建立訊息佇列; 第二個就是無限迴圈讀取訊息佇列裡的訊息。 建立訊息佇列 當建立訊息佇列的時候,需要首先呼叫Looper.prepare()靜態函式。

第73行程式碼,在sThreadLocal裡面檢查一下當前執行緒是否已經呼叫過prepare()方法了,即檢查一下和當前執行緒相關的Looper物件是否 已經建立。這裡就是保證一個執行緒只能產生一個Looper物件,如果多次呼叫會報錯。Looper物件被儲存到了執行緒本地儲存裡(ThreadLocal),只存和當前執行緒相關的內容。
對於不瞭解ThreadLocal的,自行到網上查詢一下,簡單說下,就是儲存執行緒範圍的資料。 接著看下Looper的建構函式 Looper的建構函式時私有的,同事在建構函式裡,建立了一個訊息佇列mQueue(第182行),並且把呼叫次Looper的執行緒賦值給mThread(第184行),代表這個Looper從屬的執行緒。 這樣佇列就準備好了。 讀取訊息佇列 從呼叫Looper.loop();的時候開始讀取訊息。loop()方法的原始碼如下: 108行:獲得Looper物件,就是簡單的從sThreadLocal裡面把和當前執行緒相關的Looper物件找出來,如下: 109行:從Looper物件裡找出當前執行緒的訊息佇列MessageQueue物件。 113和114行先別管,以後再講解。 直接進入while迴圈,開始讀取訊息了 117行:從訊息佇列裡讀取訊息,返回的是Message,Message代表一個訊息。如果沒有訊息,阻塞在這裡,直到有訊息返回為止。 121行:只有訊息不為null才去處理訊息 122行:如果訊息的target為null的話,就結束這次迴圈,繼續迴圈下次,target就是訊息對應的Handler物件。 130行:msg.target.dispatchMessage(msg),把訊息通過Handler分發下去,就是把訊息傳遞給Handler的回撥函式 146行:msg.recycle();當處理完一次訊息之後,要對訊息進行回收處理,因為在Message內部有一個訊息池,用來避免不停的           建立刪除訊息物件,內部只是把訊息設定成空閒狀態,以便重複利用。 MessageQueue
其實上面介紹Looper的時候,非同步訊息基本上大體就是這樣處理的。 這裡分析下MessageQueue的原始碼 訊息佇列採用排隊的方式進行訊息處理的,先到的訊息最先得到處理(還有一種情況,訊息被指定某個時刻進行處理,如果時刻不到也不會 去處理)。訊息佇列中的訊息用Message類表示,訊息是以連結串列的形式儲存,Message物件內部包含一個next變數,該變數指向下一個訊息。 訊息佇列的主要功能有兩個: 1.取出訊息 2.放進訊息 取出訊息 從Looper的原始碼中可以知道,是呼叫的訊息佇列的next()方法取出的訊息。看MessageQueue的原始碼: 從115行看起,這裡是迴圈取訊息。其實再MessageQueue.java的物件是沒有這個訊息佇列,真正的訊息佇列在C程式碼中的, 119行:private native void nativePollOnce(int ptr, int timeoutMillis)是個native函式,從C的訊息佇列中取訊息,C中有個             MessageQueue.cpp檔案,裡面定義了一個MessageQueue的C++類。呼叫C++程式碼,取出訊息,把取出的訊息賦值古java中             mMessages變數,如果沒有就會掛起等待。 121行開始,就是讀取訊息 123行:獲得當前時間 124行:把取出的訊息mMessages賦值為final修飾的msg變數,便於後面的處理。 125行:判斷取出的訊息為空 127-133行:如果到了執行時間,就會把訊息返回給Looper。 134行,如果沒到執行時間,則繼續迴圈,去取訊息。訊息佇列的讀寫不能同時進行,所以用synchronized關鍵字包圍起來。 從上面的程式碼可以看出,當時間未到或者訊息為空的時候,佇列會處於空閒狀態,如果沒有空閒時的任務的話,會繼續到佇列裡 去取訊息。其中mIdleHandlers儲存的是空閒任務,如果有空閒任務的話,空閒的時候會去執行。 看下面的程式碼:158-174行 放進訊息佇列 對應該的方法是 主要看方法被synchronized包圍的地方 205行之前都是做一些資料檢查 206-221行主要是看看這個訊息是不是佇列的頭部,如果不是頭部,needWake為false如果不是的話,就需要把這個訊息 放在隊尾,如果是隊頭或者將會變成 對頭,那麼needWake就會是true,會呼叫navtiveWake,把訊息放到C++的那個訊息佇列裡去, 最後函式返回true,表示插入佇列成功。 MesageQueue的建構函式,呼叫了nativeInit()函式,呼叫的是native的,在C程式碼中初始化一個訊息佇列。 Handler 先看下建構函式 119行:獲得當前執行緒的Looper物件,賦值給mLooper 120-123:檢查Looper物件是否為空,為空的話,肯定就沒有訊息隊列了 124行:獲得當前執行緒對應的訊息佇列 125行:mCallback是個回撥物件 再一個就是大家比較關注用handler傳送訊息 接著呼叫: 第二個引數delayMillis代表訊息延遲的時間,沒有延遲就是0 接著呼叫sendMessageAtTime 第二個引數是訊息的傳送時間 456行:把msg的target設定成當前的Handler物件,這樣每個訊息就可以關聯一個自己的Handler了,所以最後就能回撥到這個Handler的回撥函式。 457行:把訊息放進訊息佇列,然後返回成功放進去與否。 回撥Handler的函式: 其實是在Looper的loop()方法中執行的,請看 呼叫的handler物件的dispatchMessage(msg)方法,方法體如下: handleCallback(msg)先不講。 99行:呼叫了handleMesssage(msg)函式 handleMessage(msg)函式如下,就是空函式,需要程式設計師去實現: 解釋一個callback,這個是定義在Handler裡的一個內部介面,如果這個介面物件存在的話,就不會回撥handler的handleMessage(msg)方法,從94-98行程式碼可以看出。 同理當msg的callback存在的時候,也不呼叫handler的handleMessage(msg)回撥方法了,而是呼叫handleCallback(msg)方法。 回撥的Message中的callback.run()方法。其實Message中的callback就是一個Runable實現 Message解釋 Handler中有一個obtainMessage()方法,來獲取可以回收利用的Message物件。 其實呼叫的是Message.obtain(handler)方法 下面是Message類中的方法呼叫 138行呼叫了obtain()方法返回一個Message 139行:返回的Message物件的target賦值為傳過來的Handler mPoolSync就是物件鎖,防止同時被訪問而設定的 mPool就是Message物件,Message的next變數也是Message物件,是連結串列形式的,可以組成一個訊息鏈條,只要有訊息空閒就可以取出來使用。 前面的Looper執行完成之後,會有一個Message回收的呼叫,就是這個方法 clearForRecycle()把訊息還原到初始狀態,程式碼入下: 原始碼分析完了,如果感覺不錯,給個好評吧,哈哈