1 概述
前面幾篇介紹了Handler、Looper、MessageQueue的源碼實現原理,理解了原理,下一步我們要知道的就是怎麼用好這些內容.
2 獲取Message對象
首先,不建議使用new Message
方法,而是使用Message.obtain()
方法,因爲obtain()
方法內部有一套消息池機制,首先從消息池獲取可用消息,沒有的時候才新建消息,這樣就避免創建多餘的消息對象造成內存浪費。
獲取Message的方法:
1 Messgae.obtain() 優先從消息池中獲取消息,沒有空閒消息再創建。
2 Message.obtain(Message msg) 獲取一個msg消息的拷貝
3 handler.obtain() 獲取消息的同時指定這個消息的target爲調用Handler
3 主線程中使用
1、主線程中默認就已經初始化一個Looper,故可以直接new Handler()
默認的空構造方法生產一個主線程的Handler,然後通過handler的sendMessage或post方法,完成消息的發送,再通過複寫handler的 handlerMessage方法處理消息的回調。
2、有一點需要注意的是,由於sendMessage的方法處理,或者是post的Runnable,最終都是在主線程,也就是UI線程中執行的,故不要放耗時操作,避免ANR
3 子線程中獲取主線程的Looper和MessageQueue
Android的Looper默認提供了一個靜態方法Looper.getMainLooper(),用於獲取主線程的Looper對象。其實現原理,前面已經提過,主線程的looper會默認存儲在全局變量sMainLooper中,通過該方法獲取到。拿到looper對象後,就可以通過looper.getQueue()獲取該Looper的MessageQueue,通過new Handler(looper),獲取主線程的handler了。此時,拿到主線程的handler,就可以發處理UI的消息給主線程執行了。
4子線程中使用Looper
首先需要初始化一個looper
class MyLooperThread extends Thread{
public void Handler handler;
public void run(){
Looper.prepare();
handler = new Handler(){
public void handlerMessage(Message msg){
//...處理消息回調,子類實現具體業務
}
};
Looper.loop();//這裏進入循環
}
}
初始化完成後,需要再次獲取此looper,則通過Looper.myLooper()
,獲取當前線程的looper。可以通過Looper.myQueue()方法用於獲取當前線程的Looper的MessageQueue。此時也可以通過new handler()
創建一個handler(默認使用Looper.myLooper()取到的looper,也就是子線程的looper),此handler是子線程的handler,而不是UI線程的Handler,不能用此handler來更新UI
5 runOnUiThread
這個方法位於activity類中,實現也是通過主線程的handler,post一個Runnable給主線程的handler執行。所以同樣,此方法傳遞的Runnable中也不要有耗時操作。
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
6 消息的取消、查詢和發送
sendMessage發送的消息通過removeMessage(int what)來取消
post發送的消息,通過removeCallback(Runnable action)來取消
hasMessages(int what) 查詢當前是否含有what的消息
hasCallbacks(Runnable r) 查詢當前是否有r這個Runnable
postAtFrontOfQueue 把消息/Runnable插入到隊列頭部
7Handler可能引起的內存泄露
Handler作爲內部類的時候,由於內部類會持有外部類對象的一個引用,故此Handler可能導致外部類的實例對象無法被回收。此時,外部若是一個activity,則其onDestory方法一直不會被調用。
解決方式一:設置該Handler爲static,靜態方法或者屬性爲類的方法或屬性,而不是對象的方法或者屬性,故不會持有外部類的對象的引用,也就不會導致外部對象無法被回收。
解決方式二:使用弱引用實現,定義一個工具類,封裝其若引用。
public class HandlerUtil {
private static int mId = 0x1000000;
public interface MessageListener {
public void handleMessage(Message msg);
}
public static final int generateId() {
return ++ mId;
}
public static class StaticHandler extends Handler {
WeakReference<MessageListener> listener;
public StaticHandler(MessageListener listener) {
super();
this.listener = new WeakReference<MessageListener>(listener);
}
public StaticHandler(Looper looper, MessageListener listener){
super(looper);
this.listener = new WeakReference<MessageListener>(listener);
}
public StaticHandler() {
super();
}
@Override
public void handleMessage(Message msg) {
MessageListener listener = this.listener.get();
if (listener != null) {
listener.handleMessage(msg);
}
}
}
}
說明:
- 通過generateId()方法生成一個唯一的ID,App全局唯一,這樣可以防止可能發生的消息ID重複而導致的各種問題。
- 定義一個StaticHandler靜態Handler,繼承原Handler接口, 用來消除Handle可能導致的泄漏。
- StaticHandler中含有一個弱引用
WeakReference<MessageListener> listener
,在構造的時候傳入此弱引用的值。此listener必須由Activity實現該接口(推薦)或者是宿主Activity的類成員,因爲弱引用不會增加引用計數,若是使用匿名變量則會導致listener過早釋放
- StaticHandler中含有一個弱引用
- 最終調用到Handler的handlerMessage處理消息,在這個方法內部,通過this.listener.get() 獲取listener的強引用,獲取成功的時候再回調其handlerMessage方法,也就最終實現了消息的處理。
8 HandlerThread
android提供了一個HandlerThread方法,簡單封裝了子線程實現handler looper機制,方便上層使用。此HandlerThread的使用方法和主線程的使用方法類似,只是不能從通過它更新UI。