最近我在網路上查了很多關於handler的知識,但是感覺總是覺得有很多問題??
1.為什麼要使用handler?( Answer: 1)按計劃發送訊息或執行某個Runnanble(使用POST方法),類似定時器;
2)從其他線程中發送來的訊息放入訊息佇列中,避免線程衝突(常見於更新UI線程);)
2.handler是非同步,建立一個handler執行個體是建立一個新的線程嗎??(NO,沒有建立新的線程,但是為什麼沒有阻塞主線程?這是有系統管理Looper的)
3.在同一線程中,handler1發送訊息,handler1能收到訊息,handler2能收到嗎?(只能在handler1中收到)
4.在子線程中,運用主線程中定義的handler,sendMessage,主線程中得handler能收到嗎?(能)
5.在子線程中建立一個handler執行個體,並發送訊息,這樣可以嗎???(出現異常)
6.HandlerThread是什麼,什麼時候使用??(需要Looper,自己不想建立,因為HandlerThread預設有Looper,有訊息佇列)
7.Message,Looper,Handler,MessageQueue等地關係如何???
8.sendMessage和post的區別是什嗎??是否共用一個訊息佇列messageQueue??(他們是使用同一個HandlerThread;
sendMessage和post的區別就是在looper中loop()函數中得dispatchMessage()時有區別
public static final void loop() { Looper me = myLooper(); MessageQueue queue = me.mQueue; while (true) { Message msg = queue.next(); // might block //if (!me.mRun) { // break; //} if (msg != null) { if (msg.target == null) { // No target is a magic identifier for the quit message. return; } if (me.mLogging!= null) me.mLogging.println( ">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what ); msg.target.dispatchMessage(msg); if (me.mLogging!= null) me.mLogging.println( "<<<<< Finished to " + msg.target + " " + msg.callback); msg.recycle(); } } }
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
調用Handler類中以post開頭的方法可以將一個runnable對象封裝在一個Message對象中, 然後再壓入訊息佇列, 此時入隊的Message其callback欄位不為null,值就是這個runnable對象. 調用Handler對象的這些方法入隊的Message, 其target屬性會被賦值為這個handler對象.
Message的參數中也有這個參數的設定。
)
9.handler是在主線程內嗎?(不一定,一般是的,但是如果在handler的建構函式中傳入子線程的Looper,則就會在該線程中)
10.子線程中構造的的handler可以傳入主線程的Looper,就是操控主線程的訊息佇列??(可以)
帶著問題去學習handler,我想應該會很有意思的。下面就是來解決這些問題的,(*^__^*) 嘻嘻……
先介紹下handler的基本知識
Handler
Handler在android裡負責發送和處理訊息。它的主要用途:
1)按計劃發送訊息或執行某個Runnanble(使用POST方法),類似定時器;
2)從其他線程中發送來的訊息放入訊息佇列中,避免線程衝突(常見於更新UI線程);
預設情況下,Handler接受的是當前線程下的訊息迴圈執行個體(使用Handler(Looper looper)、Handler(Looper looper, Handler.Callback callback)可以指定線程),同時一個訊息佇列可以被當前線程中的多個對象進行分發、處理(在UI線程中,系統已經有一個Activity來處理了,你可以再起若干個Handler來處理)。在執行個體化Handler的時候,Looper可以是任意線程的,只要有Handler的指標,任何線程也都可以sendMessage。Handler對於Message的處理不是並發的。一個Looper
只有處理完一條Message才會讀取下一條,所以訊息的處理是阻塞形式的(handleMessage()方法裡不應該有耗時操作,可以將耗時操作放在其他線程執行,操作完後發送Message(通過sendMessges方法),然後由handleMessage()更新UI)。
Looper
Looper類用來建立訊息佇列. 每個線程最多隻能有一個訊息佇列, android中UI線程預設具有訊息佇列, 但非UI線程在預設情況下是不具備訊息佇列的. 如果需要在非UI線程中開啟訊息佇列, 需要調用Looper.prepare()方法, 在該方法的執行過程中會建立一個Looper對象, 而Looper的建構函式中會建立一個MessageQueue instance(Looper的建構函式是私人的, 在Looper類之外無法建立其對象). 此後再為該線程綁定一個Handler
instance, 然後調用Looper.loop()方法, 就可以不斷的從訊息佇列中取出訊息和處理訊息了. Looper.myLoop()方法可以得到線程的Looper對象, 如果為null, 說明此時該線程尚未開啟訊息佇列.
建立一個 Looper 對象時,會同時建立一個 MessageQueue 對象(一個looper對應一個MessageQueue)。除了主線程有預設的 Looper ,其他線程預設是沒有 MessageQueue 對象的,所以,不能接受 Message 。如需要接受,自己定義 一個 Looper 對象 ( 通過 prepare 函數 ), 這樣該線程就有了自己的 Looper 對象和 MessageQueue 資料結構了。
Looper 從 MessageQueue 中取出 Message 然後,交由 Handler 的 handleMessage 進行處理。處理完成後,調用 Message.recycle() 將其放入 Message Pool 中。
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }
Message
Message :訊息對象, Message Queue 中的存放的對象。一個 Message Queue 中包含多個 Message 。 Message 執行個體對象的取得,通常使用 Message 類裡的靜態方法 obtain(), 該方法有多個重載版本可供選擇;它的建立並不一定是直接建立一個新的執行個體,而是先從 Message Pool( 訊息池 ) 中看有沒有可用的 Message 執行個體,存在則直接取出返回這個執行個體。如果 Message Pool
中沒有可用的 Message 執行個體,則才用給定的參數建立一個 Message 對象。調用 removeMessages() 時,將 Message 從 Message Queue 中刪除,同時放入到 Message Pool 中。除了上面這種方式,也可以通過 Handler 對象的 obtainMessage() 擷取 一個 Message 執行個體。
Message類用於表示訊息. Message對象可以通過arg1, arg2, obj欄位和setData()攜帶資料, 此外還具有很多欄位.
when欄位決定Message應該何時出對處理,;
target欄位用來表示將由哪個Handler對象處理這個訊息;
next欄位表示在訊息佇列中排在這個Message之後的下一個Message;
callback字段如果不為null表示這個Message封裝了一個runnable對象;
what欄位表示code, 即這個訊息具體是什麼類型的訊息. 每個what都在其handler的namespace中, 我們只需要確保將由同一個handler處理的訊息的what屬性不重複就可以.
MessageQueue
MessageQueue類用於表示訊息佇列. 隊列中的每一個Message都有一個when欄位, 這個欄位用來決定Message應該何時出對處理. 訊息佇列中的每一個Message根據when欄位的大小由小到大排列, 排在最前面的訊息會首先得到處理, 因此可以說訊息佇列並不是一個嚴格的先進先出的隊列。 主線程建立時,會建立一個預設的 Looper 對象,而 Looper 對象的建立,將自動建立一個 Message Queue 。其他非主線程,不會自動建立 Looper ,要需要的時候,通過調用 prepare
函數來實現。
將訊息壓入訊息佇列:
Message對象的target欄位關聯了哪個線程的訊息佇列, 這個訊息就會被壓入哪個線程的訊息佇列中.
a 調用Handler類中以send開頭的方法可以將Message對象壓入訊息佇列中,;
調用Handler類中以post開頭的方法可以將一個runnable對象封裝在一個Message對象中, 然後再壓入訊息佇列, 此時入隊的Message其callback欄位不為null, 值就是這個runnable對象. 調用Handler對象的這些方法入隊的Message, 其target屬性會被賦值為這個handler對象.
b. 調用Message對象的sendToTarget()方法可以將其本身壓入與其target欄位(即handler對象)所關聯的訊息佇列中.
從訊息佇列中取出訊息並處理訊息:
所有在訊息佇列中的訊息, 都具有target欄位. 訊息是在target所關聯的線程上被取出和處理的.
1. 如果取出的Message對象的callback欄位不為null, 那麼就調用callback欄位的run()方法(callback欄位的類型是runnable). 注意此時並不開啟一個新的線程運行run()方法, 而是直接在handler對象(即Message的target欄位)所關聯的線程上運行.
2. 如果取出的Message對象的callback欄位為null, 且Handler對象中的callback欄位也為null, 那麼這個訊息將由Handler對象的handleMessage(msg)方法處理. 注意Message對象的callback欄位是Runnable類型的而Handler對象的callback欄位是Callback類型的, Handler對象的callback欄位是在建立Handler instance的時候指定的, 如果沒有指定則這個欄位為null, 詳見Handler類的四個構造方法.
3. 如果取出的Message對象的callback欄位為null, 且Handler對象中的callback欄位不為null, 那麼這個訊息將由Handler對象中的callback欄位的handleMessage方法處理.
線程間通訊
有了以上的敘述, 線程間的通訊也就好理解了. 假如一個handler關聯了A線程上的訊息佇列, 那麼我們可以在B線程上調用handler的相關方法向A線程上的訊息佇列壓入一個Message, 這個Message將在A線程上得到處理.
上代碼:
public class HandlerDemo extends Activity {private final static int MESSAGE_ID = 5;private TestHandler1 mTestHandler1;private TestHandler2 mTestHandler2;/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);mTestHandler1 = new TestHandler1(); mTestHandler2 = new TestHandler2();Button btn = (Button) findViewById(R.id.clcik);btn.setOnClickListener(new View.OnClickListener(){@Overridepublic void onClick(View v) {// TODO Auto-generated method stubnew Thread(new Runnable(){ //注1@Overridepublic void run() {// TODO Auto-generated method stub// 發送訊息// 直接從handler擷取message對象,當然也可以new一個,但是推薦前一個Message msg = mTestHandler1.obtainMessage(); //注2Bundle b = new Bundle();b.putString("color", "red");// 給handler發送的資料,放入message中msg.setData(b);msg.what = MESSAGE_ID;mTestHandler1.sendMessage(msg); //注1}}).start();}});}private class TestHandler1 extends Handler //注1{@Overridepublic void handleMessage(Message msg) {// TODO Auto-generated method stubsuper.handleMessage(msg);if (msg.what == MESSAGE_ID) { //注3Bundle b = msg.getData();String colorStr = b.getString("color");Log.d("HandlerDemo", "TestHandler1---------"+colorStr);// mainThread 1//Log.e("MyHandler handler-->", Thread.currentThread().getId()//+ "");//Log.e("MyHandler handler-->", Thread.currentThread().getName()//+ "");}}}private class TestHandler2 extends Handler //注1{@Overridepublic void handleMessage(Message msg) {// TODO Auto-generated method stubsuper.handleMessage(msg);if (msg.what == MESSAGE_ID) { //注3Bundle b = msg.getData();String colorStr = b.getString("color");Log.d("HandlerDemo", "TestHandler2---------"+colorStr);// mainThread 1//Log.e("MyHandler handler-->", Thread.currentThread().getId()//+ "");//Log.e("MyHandler handler-->", Thread.currentThread().getName()//+ "");}}}}
注1:
繼承於handler的兩個子類,TestHandler1,TestHandler2;並在主線程中分別建立兩個對象;
建立一個子線程,在子線程用主線程中定義的mTestHandler1對象,發送訊息
Message msg = mTestHandler1.obtainMessage(); //注2Bundle b = new Bundle();b.putString("color", "red");// 給handler發送的資料,放入message中msg.setData(b);msg.what = MESSAGE_ID;mTestHandler1.sendMessage(msg); //注1
注2::
這裡最好用
Message msg = mTestHandler1.obtainMessage();
方式擷取message對象,當然可以new 一個message對象;
setdata,是把資料塞入message對象中進行傳遞;
what參數是為了區分是發給同一個handler的訊息;
結果:
這樣就可以解決前面提出的幾個問題3,4兩個問題;
handler是從哪個handler發送,就必須從哪個handler接收; mTestHandler1發送,TestHandler1接收,TestHandler2沒有接收到;
在子線程中能向主線程中定義的handler對象發送Message;
如果在子線程中定義handler,會如何???
把mTestHandler1.sendMessage(msg);替換成如下代碼:
new Handler() //注4{@Overridepublic void handleMessage(Message msg) {// TODO Auto-generated method stubsuper.handleMessage(msg);if (msg.what == MESSAGE_ID) { Bundle b = msg.getData();String colorStr = b.getString("color");Log.d("HandlerDemo", "內部類---------"+colorStr);}}}.sendMessage(msg);
這樣程式出席異常,因為沒有調用Looper.prepare(),沒有Looper執行個體,所以.....但是這也說明一個普通的子線程沒有Looper執行個體,沒有訊息迴圈;
對於這個問題,可以用HandlerThread,就不會出現異常;因為HandlerThread都有Looper執行個體的。。。。
// (3)new HandlerThread TestHandlerThread testHandlerThread = new TestHandlerThread("testHandlerThread");testHandlerThread.start();
private class TestHandlerThread extends HandlerThread{ // 注5public TestHandlerThread(String name) {super(name);// TODO Auto-generated constructor stub}@Overridepublic void run() {// TODO Auto-generated method stubsuper.run();Message msg = mTestHandler1.obtainMessage(); Bundle b = new Bundle();b.putString("color", "red");// 給handler發送的資料,放入message中msg.setData(b);msg.what = MESSAGE_ID;Looper looper = this.getLooper();new Handler(looper){@Overridepublic void handleMessage(Message msg) {// TODO Auto-generated method stubsuper.handleMessage(msg);Log.d("HandlerDemo", "TestHandlerThread---------");}}.sendMessage(msg);}}
這樣就解決了問題5.6;
在子線程中實現handler,並產生一個handler對象,並在子線程中建立一個訊息佇列(就是Looper對象,通過prepare(),loop())(或者得到主線程中得訊息佇列,操控主線程的訊息佇列Looper.getMainLooper()),這樣都是可以的,但是沒有實在的意義。。。
注意:
子線程預設沒有訊息佇列。。。。。。。
handler的發送和接收的handler是同一個對象,不管是否在同一個線程中。。。
handler的post和sendmessage處理的是同一個訊息佇列(這個可以看源碼就看到的。。。。。)
post在dispatch時,是通過判斷callback處理的,調用 handleCallback(msg);而sendmessage是調用handMessage()處理;
有問題留言。。。