歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Handler的相關知識和應用

Handler的相關知識和應用

日期:2017/3/1 10:49:48   编辑:Linux編程

最近我在網路上查了很多關於handler的知識,但是感覺總是覺得有很多問題??

1.為什麼要使用handler?

2.handler是異步的,創建一個handler實例是創建一個新的線程嗎??(NO,沒有創建新的線程,但是為什麼沒有阻塞主線程?這是有系統管理Looper的

3.在同一線程中,handler1發送消息,handler1能收到消息,handler2能收到嗎?

4.在子線程中,運用主線程中定義的handler,sendMessage,主線程中得handler能收到嗎?

5.在子線程中創建一個handler實例,並發送消息,這樣可以嗎???

6.HandlerThread是什麼,什麼時候使用??

7.Message,Looper,Handler,MessageQueue等地關系如何???

8.sendMessage和post的區別是什麼??是否共用一個消息隊列messageQueue??

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 中。

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方法處理.

Copyright © Linux教程網 All Rights Reserved