安卓消息處理機制(Looper,Handler,Message)

1.前言

android消息處理機制很基礎(chǔ),也很重要。一般耗時操作需要放到子線程中去執(zhí)行,執(zhí)行完后,需要把結(jié)果反饋給主線程,在主線程中更新ui。比如:在子線程中加載一張網(wǎng)絡(luò)圖片,加載成功后,在主線程中顯示圖片。那么,子線程怎樣把結(jié)果反饋給主線程?這就用到了我們今天講到的消息處理。

2.簡要說明消息處理過程

消息處理機制主要涉及四個類:Looper,Message,MessageQueue,Handler。過程如下:

一個線程有且只有一個Looper對象,Looper是循環(huán)的意思,Looper對象創(chuàng)建自己的消息隊列MessageQueue,當(dāng)有消息message產(chǎn)生時,message會被壓入隊列message queue。Looper一直循環(huán)做如下工作:從message queue中取出一個 msg,handler來處理。借用一張圖:

image

具體怎么處理下面會詳細(xì)說明。

3. Looper

先來說說Looper。

Looper主要作用:

  1. 與當(dāng)前線程綁定,保證一個線程只會有一個Looper實例,同時一個Looper實例也只有一個MessageQueue(創(chuàng)建消息隊列)。

  2. loop()方法不斷從MessageQueue中去取消息,交給消息Message的target屬性的dispatchMessage()去處理。

Looper中的變量:

// 每個線程中的Looper對象其實是一個ThreadLocal,即線程本地存儲(TLS)對象
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
final MessageQueue mQueue;//looper中的消息隊列
final Thread mThread;//綁定的線程

Looper通過prepare()方法來創(chuàng)建looper對象:

//quitAllowed:消息循環(huán)是否可以退出(主線程傳進(jìn)去的是false)
private static void prepare(boolean quitAllowed) {
        //如果一個線程中已經(jīng)有l(wèi)ooper對象,再創(chuàng)建就會拋出異常,也就是說,一個線程只能有一個looper對象
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

調(diào)用這個方法之后,需調(diào)用loop()方法來從消息隊列中取出消息:

public static void loop() {
        //得到當(dāng)前線程的Looper實例
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //獲取消息隊列
        final MessageQueue queue = me.mQueue;
        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        //looper開始無限循環(huán)
        for (;;) {
            Message msg = queue.next(); // 取出對列頭的message(也可以叫task)
            if (msg == null) {
                //如果消息隊列中沒有task,就退出循環(huán)
                return;
            }
            //日志
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
            //使用調(diào)用 msg.target.dispatchMessage(msg);把消息交給msg的target的dispatchMessage方法去處理。
            // Msg的target是什么呢?其實就是handler對象,下面會進(jìn)行分析。
            msg.target.dispatchMessage(msg);
            //日志
            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }
            /*
            clearCallingIdentity這個可以看成是安全性代碼,也可以看成是調(diào)試代碼
            作用是確定當(dāng)前這個looper所在的“線程”是否一直在同一個“進(jìn)程”里,如果進(jìn)程變多半是說明這個線程運行在某種跨進(jìn)程代碼里。
            比如說你通過AIDL調(diào)用stub,遠(yuǎn)程那邊接到之后啟動一個線程,就有可能觸發(fā)ident != newIdent了
            */
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }
            //釋放消息占據(jù)的資源
            msg.recycleUnchecked();
        }
    }
    
    public static MessageQueue myQueue() {
        return myLooper().mQueue;
    }
    
    /*
    * 得到當(dāng)前線程的Looper實例
    * */
    public static Looper myLooper() {
        return sThreadLocal.get();
    }

如果要結(jié)束loop()循環(huán),可以調(diào)用quit()退出循環(huán)。

public void quit() {
        mQueue.quit(false);
    }

注意:

  1. looper方法必須在prepare方法之后運行

  2. 每個線程有且最多只能有一個Looper對象,它是一個ThreadLocal;一個消息隊列(如果一個線程已經(jīng)有l(wèi)ooper對象,再創(chuàng)建會拋異常)

  3. Looper內(nèi)部有一個消息隊列,loop()方法調(diào)用后線程開始不斷從隊列中取出消息執(zhí)行

  4. Looper使一個線程變成Looper線程。

另外,Looper中還有一些方法看一下:

    //獲取message queue    
    public static MessageQueue myQueue() {
        return myLooper().mQueue;
    }

    /*
    * 得到當(dāng)前線程的Looper實例
    * */
    public static Looper myLooper() {
        return sThreadLocal.get();
    }

/*
    * 給主線程初始化一個MainLooper,MainLooper一般是系統(tǒng)create,所以自己永遠(yuǎn)不用調(diào)用,知道有這么個東西就好了。
    * */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    /*
    * 得到主線程的Looper實例
    * */
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

4.Message

上面講到,從looper中取出一個msg后,通過這句代碼交給msg的target來處理消息msg.target.dispatchMessage(msg)。

target是什么?

我們來看看Message類有什么東西。先看看有哪些變量:

Handler target;//target是處理消息的Handler對象
Runnable callback;//回調(diào)callback

private static Message sPool;
private static int sPoolSize = 0; /

//message內(nèi)容
public int what;
Bundle data;
public Object obj;
public int arg1;
public int arg2;

獲取Message實例可以直接new 一個Message,也可以通過調(diào)用Message的obtain()方法,Message中obtain()方法有很多,這里只看一個。

public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

所以,Message類中有一個Handler類型的變量target,來處理消息。真正處理消息的,是Handler類。下面我們來看Handler類。

注意:

產(chǎn)生一個Message對象,可以new,也可以使用Message.obtain()方法;

兩者都可以,但是更建議使用obtain方法,因為Message內(nèi)部維護(hù)了一個Message池用于Message的復(fù)用,避免使用new 重新分配內(nèi)存。

5.Handler

依舊先看看變量:

final MessageQueue mQueue;//handler所在的線程的消息隊列
final Looper mLooper;//handler所在線程的looper對象
//這個callback是一個接口,create handler對象的時候需要實現(xiàn)它。里面有一個handlerMessage()方法,
//handlerMessage()方法就是我們需要自己實現(xiàn)的處理消息的方法。
final Callback mCallback;

CallBack接口:

public interface Callback {
        public boolean handleMessage(Message msg);
    }

我們通常這樣用:

Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    break;
            }
        }
    };

熟悉吧?這個handlerMessage()就是Handler類中的interface mCallback 里的handlerMessage()方法。

前面說過,looper從消息隊列中取出一個message,通過message的target.dispatchMessage(msg)(Handler)來處理該消息。那么,Handler是如何與MessageQueue聯(lián)系上的,它在子線程中發(fā)送的消息(一般發(fā)送消息都在非UI線程)又是怎么發(fā)送到MessageQueue中的?

我們來看看Handler的構(gòu)造方法:

public Handler(Callback callback, boolean async) {
        //不用管
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                        klass.getCanonicalName());
            }
        }
        //得到當(dāng)前線程中的looper對象
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
        }
        //得到消息隊列
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

ok,上面疑問搞清楚了。我們接著來看之前一直說的dispatchMessage(msg)方法。

/*
* 這個方法處理消息
* 如果msg的callback和target(handler)都有值,會執(zhí)行哪個?
* 通過getPostMessage(Runnerable r)方法可以知道,r即指定msg的callback。
* */
public void dispatchMessage(Message msg) {
    //先執(zhí)行msg的callback
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        //如果msg的callback為空,則執(zhí)行msg的handler對象target的回調(diào)mCallback。
        // 這個mCallback就是我們平時創(chuàng)建handler時實現(xiàn)的接口handleressage()。
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

好了,到這里,消息處理就說完了。handler類里有一些發(fā)送消息的方法,post(Runnable r),sendMessageDelayed(Message msg, long delayMillis)sendMessageAtTime(Message msg, long uptimeMillis),postAtTime(Runnable r, long uptimeMillis)等等,它們的作用都是把一個msg壓入message queue中等待處理。如果想知道 post和sendMessage的區(qū)別,怎么將msg壓入message queue,這些方法的具體實現(xiàn)等,可以看我的博客 postDelayed, sendMessageAtTime等handler發(fā)送消息方法總結(jié)

參考資料:

  1. Android 異步消息處理機制 讓你深入理解 Looper、Handler、Message三者關(guān)系

  2. android的消息處理機制(圖+源碼分析)——Looper,Handler,Message

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容