1. 程式人生 > >Handler機制剖析

Handler機制剖析

Handler剖析-介紹

原文:

A Handler allows you to send and process {@link Message} and Runnable objects associated with a thread's {@link MessageQueue}.  Each Handler instance is associated with a single thread and that thread's message queue.  When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on,it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

譯:

Handler允許用來發送和處理一個與執行緒關聯的MessageQueue中的Message和Runnable物件,每個Handler例項都只能與一個執行緒和執行緒的MessageQueue繫結在一起,當建立一個Handler的時候.
由此一來,Handler可以用來傳遞Message和Runnable到MessageQueque和執行訊息佇列中的Message和Runnable.

Handler剖析-建構函式


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

        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;
    }
這是主要建構函式,在此函式中首先對匿名內部類等進行了日誌警告,在我們建立Handler例項時如果為非靜態的物件就會出現類似這樣的日誌警告:
The following Handler class should be static or leaks might occur:xxx
其次,通過Looper.myLooper()獲取一個Looper例項根據此例項拿到備用的MessageQueue
總結:在這個方法中進行了Looper,MessageQueue進行了關聯.

Handler剖析-sendMessageAtTime

介紹:返回true代表Message被成功加入到MessageQueue,否則返回false,通常返回false是因為Looper在處理MessageQueue時退出了,返回true並不代表Looper一定會處理這個Message如果Looper在傳遞Message之前退出.
所有的postXxx(xxx)方法和sendXxxMessage(xxx)方法最終都會呼叫此方法
在此方法中需要兩個引數(Message,Long):訊息實體和訊息被傳遞的具體時間,單位:毫秒


public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

通過原始碼可知,此方法中是拿到了一個MessageQueue物件後最終呼叫 enqueueMessage(MessageQueue, Message, Long)進行處理.

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

msg.target:一個Handler物件
MessageQueue.enqueueMessage(Message, Long):在MessageQueue中進行處理,後面講解

Handler剖析-dispatchMessag

 <span style="font-size:18px;">/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }</span>
注:通過Message中可以得知msg.callback是一個Runnable物件

Handler剖析-總結

在Handler中大致有三個作用
1.與執行緒中的Looper,MessageQueue關聯
2.傳送Message到MessageQueue
3.接收來自MessageQueue的Message進行回撥處理

Message剖析-介紹

原文:

Defines a message containing a description and arbitrary data object that can be sent to a {@link Handler}.  This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases.  

譯文:

一個訊息包含一個描述資訊和一個任意資料物件,可以通過Handler來進行傳送,此外,這個物件中包含兩個額外的int欄位(arg1,arg2)和一個允許你在大多數情況下都不做分配的額外的物件欄位obj.
注:Message是一個連結串列結構,其中有一個非常重要的欄位next,指向下一個Message物件

Message剖析-what,arg1,arg2,obj

what:使用者自定義的訊息code,以便在接收訊息時知道這個訊息是什麼,每個Handler都有自己的域名和對應的訊息code,因此使用者不需要擔心此訊息與其他Handler的訊息衝突.
arg1,arg2:如果只需要儲存較小的整型資料時可以用來替代使用setData(Bundle).獲取時通過getData()來進行獲取.
obj:傳送一個任意物件給接收者

Message剖析-obtain()


/**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    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剖析-obtain(Handler)

<span style="font-size:18px;">public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;

        return m;
    }</span>

其他操作均和obtain()方法一樣,唯一不同的是在此將需要傳送的Handler進行了賦值操作,因此後面只需要呼叫sendToTarget()方法即可.但是此方法不支援Delayed傳送,即:即可傳送訊息

MessageQueue剖析


boolean enqueueMessage(Message,Long)方法:
首先檢測訊息的合法性:是否已經在處理中和是否有處理它的Handler,然後判斷 mQuiting 是否中止了,如果沒有則根據訊息處理時間排序將訊息插入連結串列中的合適位置。

Looper剖析-介紹

一個線上程中執行一個訊息迴圈的類,執行緒預設情況下是沒有與Looper關聯的,在建立Looper時,線上程中呼叫prepare()方法開始執行迴圈,這樣就能處理message了,直到Looper停止為止.
Looper剖析-prepare(boolean)

<span style="font-size:18px;">private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }</span>

一個執行緒只有呼叫一次此方法,因此一個執行緒只允許有一個Looper,sThreadLocal是一個儲存Looper的工具類.Looper中儲存了一個MessageQueue物件.

Looper剖析-loop()


獲取一個Looper物件,通過Looper物件拿到MessageQueue,然後在無限迴圈獲取Message物件進行回撥給Handler中處理.

總結

1.一個執行緒中只能存在一個Looper,當一個執行緒建立時並不會建立一個Looper,而是在建立Looper後與當前執行緒繫結,且只能有一個.
2.Looper先後呼叫prepare()方法來儲存一個Looper例項化物件,並且這個Looper例項物件中有且包含一個MessageQueue,並用ThreadLocal來儲存Looper.
3.Handler在例項化時可以獲取到當前執行緒中唯一的Looper,並且拿到MessageQueue,如此一來Handler與Looper,MessageQueue進行了關聯.
4.Looper通過loop()不斷從MessageQueue的next()方法獲取Message,並通過Message獲取到target(即Handler)來回調並達到處理訊息的機制(dispatchMessage(Message)).