Looper Handler MessageQueue Message理解

摘自:http://coolxing.iteye.com/blog/1208371

總結的很好

多線程在所有編程語言中都是比較難以理解和掌握的, 這幾天上網看了很多android多線程方面的資料, 在這裏做一些總結.


什麼時候使用多線程:

1. 耗時操作使用多線程, 耗時操作放在UI線程中會導致用戶的操作無法得到響應.

2. 阻塞操作使用多線程, 理由同上.

3. 多核CUP的設備使用多線程, 可以有效提高CPU的利用率.

4. 並行操作使用多線程.


android中的多線程模型主要涉及的類有:Looper, Handler, MessageQueue, Message等. 


Looper類用來創建消息隊列. 每個線程最多只能有一個消息隊列, android中UI線程默認具有消息隊列, 但非UI線程在默認情況下是不具備消息隊列的. 如果需要在非UI線程中開啓消息隊列, 需要調用Looper.prepare()方法, 在該方法的執行過程中會創建一個Looper對象, 而Looper的構造函數中會創建一個MessageQueue instance(Looper的構造函數是私有的, 在Looper類之外無法創建其對象).  此後再爲該線程綁定一個Handler instance, 然後調用Looper.loop()方法, 就可以不斷的從消息隊列中取出消息和處理消息了. Looper.myLoop()方法可以得到線程的Looper對象, 如果爲null, 說明此時該線程尚未開啓消息隊列.


Handler類用於處理消息. 該類具有四個構造函數:

1. public Handler(). 創建好的Handler instance將綁定在代碼所在的線程的消息隊列上, 因此一定要確定該線程開啓了消息隊列, 否則程序將發生錯誤. 使用這個構造函數創建Handler instance, 一般來說, 我們需要重寫Hanler類的handleMessage()方法, 以便在之後的消息處理時調用.

2. public Handler(Callback callback). Callback是Handler內部定義的一個接口, 因此想要使用這個構造函數創建Handler對象, 需要自定義一個類實現Callback接口, 並重寫接口中定義的handleMessage()方法. 這個構造函數其實與無參的構造函數類似, 也要確保代碼所在的線程開啓了消息隊列. 不同的是在之後處理消息時, 將調用callback的handleMessage()方法, 而不是Handler對象的handleMssage()方法.

3. public Handler(Looper looper). 這個構造函數表示創建一個Handler instance, 並將其綁定在looper所在的線程上. 此時looper不能爲null. 此時一般也需要重寫Hanler類的handleMessage()方法

4. public Handler(Looper looper, Callback callback). 可以結合2和3理解.


MessageQueue類用於表示消息隊列. 隊列中的每一個Message都有一個when字段, 這個字段用來決定Message應該何時出對處理. 消息隊列中的每一個Message根據when字段的大小由小到大排列, 排在最前面的消息會首先得到處理, 因此可以說消息隊列並不是一個嚴格的先進先出的隊列.


Message類用於表示消息. Message對象可以通過arg1, arg2, obj字段和setData()攜帶數據, 此外還具有很多字段. when字段決定Message應該何時出對處理, target字段用來表示將由哪個Handler對象處理這個消息, next字段表示在消息隊列中排在這個Message之後的下一個Message, callback字段如果不爲null表示這個Message包裝了一個runnable對象, what字段表示code, 即這個消息具體是什麼類型的消息. 每個what都在其handler的namespace中, 我們只需要確保將由同一個handler處理的消息的what屬性不重複就可以.


將消息壓入消息隊列: Message對象的target字段關聯了哪個線程的消息隊列, 這個消息就會被壓入哪個線程的消息隊列中.

1. 調用Handler類中以send開頭的方法可以將Message對象壓入消息隊列中, 調用Handler類中以post開頭的方法可以將一個runnable對象包裝在一個Message對象中, 然後再壓入消息隊列, 此時入隊的Message其callback字段不爲null, 值就是這個runnable對象. 調用Handler對象的這些方法入隊的Message, 其target屬性會被賦值爲這個handler對象.

2. 調用Message對象的sendToTarget()方法可以將其本身壓入與其target字段(即handler對象)所關聯的消息隊列中. 


將未來得及處理的消息從消息隊列中刪除:

調用Handler對象中以remove開頭的方法就可以.


從消息隊列中取出消息並處理消息: 所有在消息隊列中的消息, 都具有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線程上得到處理.

發佈了4 篇原創文章 · 獲贊 6 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章