Handler Looper MessageQueue的應用

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);
            }
        }
    }
}

說明:

    1. 通過generateId()方法生成一個唯一的ID,App全局唯一,這樣可以防止可能發生的消息ID重複而導致的各種問題。
    1. 定義一個StaticHandler靜態Handler,繼承原Handler接口, 用來消除Handle可能導致的泄漏。
    1. StaticHandler中含有一個弱引用WeakReference<MessageListener> listener,在構造的時候傳入此弱引用的值。此listener必須由Activity實現該接口(推薦)或者是宿主Activity的類成員,因爲弱引用不會增加引用計數,若是使用匿名變量則會導致listener過早釋放
    1. 最終調用到Handler的handlerMessage處理消息,在這個方法內部,通過this.listener.get() 獲取listener的強引用,獲取成功的時候再回調其handlerMessage方法,也就最終實現了消息的處理。

8 HandlerThread

android提供了一個HandlerThread方法,簡單封裝了子線程實現handler looper機制,方便上層使用。此HandlerThread的使用方法和主線程的使用方法類似,只是不能從通過它更新UI。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章