標籤:android handler looper message 線程通訊
題外話:
說來有些慚愧,對於這三者的初步認識居然是在背面試題的時候。那個時候自己接觸Android的時間還不長,學習的書籍也就是比較適合入門的《瘋狂Android講義》,當然在學到Handler這一部分的時候,書中也是有提到一些簡單樣本,後來在工作中需要用到這個MessageQueue的時候才開始真正琢磨了一下這三者的聯絡。如果想要對這三者好好理解一番,個人還是比較推薦《深入理解Android卷Ⅰ》。以下對這三者之間的恩怨糾葛的介紹和分析也是參考這本書的相關章節,算是一篇讀書筆記吧。
概述:
Android的訊息傳遞機制是另一種形式的“事件處理”,這種機制主要是為瞭解決Android應用中的多線程問題——Android平台只允許UI線程修改Activity中的UI組件,這就使得新啟動的線程無法去動態修改介面組件中的屬性值。但是我們的程式介面不可能是一個靜態呈現,所以這就必須用到本部落格中提到的三個大類了。
簡單樣本:代碼展示:
public class LooperThreadActivity extends Activity { private final int MSG_HELLO = 0; private Handler mHandler; private CustomThread mThread = null; private static final String TAG = LooperThreadActivity.class.getSimpleName(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mThread = new CustomThread(); mThread.start(); Button sendButton = (Button) findViewById(R.id.send_button); final EditText contentEditText = (EditText) findViewById(R.id.content_edittext); sendButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String msgText = contentEditText.getText().toString(); sendMessage(msgText); } }); } private void sendMessage(String content) { Toast.makeText(this, "send msg: " + content, 0).show(); // TODO 1.向MessageQueue中添加訊息 // 通過Message.obtain()來從訊息池中獲得空訊息對象,以節省資源 Log.i(TAG, "------------> send msg 1."); Message msg = mHandler.obtainMessage(MSG_HELLO, content); msg.sendToTarget(); Log.i(TAG, "------------> send msg 2."); Message msg2 = mHandler.obtainMessage(MSG_HELLO, content + "2"); msg2.sendToTarget(); } class CustomThread extends Thread { @Override public void run() { Looper.prepare(); Log.i(TAG, "------------> loop.pre."); mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case MSG_HELLO: Log.i(TAG, "------------> receive msg."); Toast.makeText(LooperThreadActivity.this, "receive msg: " + (String) msg.obj, 0).show(); } } }; Looper.loop(); } }}
運行效果展示:
Log結果展示:
樣本結果分析:
大家可以看到我做了連續兩次的添加訊息資料,在結果中也有很好的體現,不過Looper.prepare();和Handler之間的內容卻只執行了一次。這是因為我們自訂的線程CustomThread只被start了一次,且start過後一直存在,沒有被銷毀,所以Looper一直存在,MessageQueue一直存在,從而保證了一個Thread只能有一個Looper對象。對於這一點下面會用源碼進行進一步的說明。
機制淺析:
就應用程式而言,Android系統中Java的應用程式和其他系統上相同,都是靠訊息驅動來工作的。Android系統中的訊息驅動離不開Looper、Handler和Message這三者,雖說不上哪個更重要一些,不過相對突出的的確是Looper。下面就對這些類逐一地介紹。
Looper類分析:1.Looper.prepare();
跟蹤prepare()進入Android的源碼,我們可以發現以下原始碼:
private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
sThreadLocal定義:
// sThreadLocal.get() will return null unless you've called prepare().static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
從以上源碼中我們可以看到,在調用prepare的線程中,設定了一個Looper對象,這個Looper對象就儲存在這個調用線程的TLV中。而Looper對象內部封裝了一個訊息佇列。也就是說prepare通過ThreadLocal機制,把Looper和調用線程關聯在了一起。
2.Looper.loop();
跟蹤loop()進入Android的源碼(此處刪除了一些暫時不太關聯的代碼):
public static void loop() {final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } msg.target.dispatchMessage(msg); msg.recycle(); }}通過上面的分析,Looper有以下幾個作用:
- 封裝了一個訊息佇列.
- prepare函數把當前的Looper和調用prepare的線程(即最終的處理線程)綁定在了一起.
- 處理線程調用loop,處理來自該訊息佇列中的訊息.
當事件來源向這個Looper發送訊息的時候,其實就是把訊息加到這個Looper的訊息列隊裡了。那麼,該訊息就將由和Looper綁定的處理來處理。
Handler類分析:
學習Handler之初先來認識一下Handler中所包含的部分成員:
final MessageQueue mQueue;final Looper mLooper;final Callback mCallback;
在Handler類中,它的建構函式會把Handler中的訊息佇列變數最終都會指向Looper的訊息佇列。由於是被指向,那麼Handler中的訊息佇列其實就是某個Looper的訊息佇列。
Looper和Handler的同步關係說明及HandlerThread的介紹:
Looper和Handler之間其實是存在著同步關係的。這裡對它們之間的同步關係不做過多介紹,如果想瞭解可以參看《深入理解Android卷Ⅰ》第128頁。筆者在此只提出一個提醒點:由於HandlerThread完美地解決了Looper和Handler同步過程中可能出現的null 指標異常問題,所以在以後的開發過程中,我們還是多用HandlerThread吧。當然如果不想使用它,那就請使用鎖機制來健壯你的代碼吧,不過這就可能會落下重複造輪子的口舌了。
參考資料:
1.《瘋狂Android講義》
2.《深入理解Android卷Ⅰ》
3.網路資源:http://www.cnblogs.com/codingmyworld/archive/2011/09/14/2174255.html
4.網路資源:http://www.cnblogs.com/bastard/archive/2012/06/08/2541944.html
5.網路資源:http://blog.csdn.net/mylzc/article/details/6771331
尾聲:
雖然已經“稀裡糊塗”到了結尾,不過對Looper、Handler和Message的認識的確進了一大步。希望看完本文的你也有所收穫。
Android的訊息處理機制——Looper,Handler和Message淺析