Handler消息發(fā)送處理機制源碼解析

Handler

1. Handler 是什么?

Handler 是 android 中消息處理機制。一個Handler 會對應一個 Thread 和 該 Thread 綁定的 MessageQueue,Handler 會將 Message 發(fā)送到 MessageQueue 中,通過 Looper 輪訓器的方式從消息隊列中輪訓消息,每次輪訓到的消息都交由 Handler 去處理這個消息。

2. Handler 的作用是什么?

可以方便地將任務切換到 Handler 所在的線程中去執(zhí)行。若是在子線程中進行耗時操作完成后需要更新 UI, 那么就可以利用 Handler 消息機制的特性對主線程UI進行更新。前提就是 Handler 是在主線程創(chuàng)建。

3. Message消息

3.1 什么是 Message ?

Message 是 Handler 發(fā)送消息的載體,它通過 Handler 發(fā)送出去然后 Looper 會在指定的 MessageQueue 去取出這個 Message,最后再將該消息交給指定的 Handler 去處理。

3.2 Message工作原理

每一個Message內部都有一個next屬性指向下一個 Message,這樣就構成了一個鏈表結構,而 sPool 是一個持有 Message 鏈表頭部的引用,是一個 Message 類型的變量。每次通過obtain 方式獲取到 Message 之后 sPoolSize--,這種方式獲取 Message 好處在于重復利用這些消息,節(jié)約內存開銷。這里需要注意的是在 5.2小節(jié)中,loop()的最后一段代碼:msg.recycleUnchecked();它是負責對這些消息進行回收操作的。

3.3 創(chuàng)建消息方式一

//默認構造
public Message() {
}

//創(chuàng)建Message對象
Message msg = new Message();

3.4 創(chuàng)建消息方式二

public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                //sPool持有Message 池中的鏈表頭的Message
                Message m = sPool;
                sPool = m.next;//指向到下一個Message
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

//創(chuàng)建Message對象
Message msg = Message.obtain();

4. 消息隊列 MessageQueue 的工作原理

消息隊列適用于管理消息的,主要就是消息的插入和移除操作。內部的數(shù)據(jù)結構采用的是鏈表結構,鏈表的特點在于添加和移除比較方便。通過 Hanadler 發(fā)送的消息最終會調用 MessageQueue 中 boolean enqueueMessage(Message msg, long when) 將消息插入。

4.1 boolean enqueueMessage(Message msg, long when) 插入一條消息

往鏈表中添加一條數(shù)據(jù),若當前鏈表沒有數(shù)據(jù)則該 Message 做為 head,若是鏈表有數(shù)據(jù)則比對所有的消息的 when 值,將Message 插入到指定的位置。

boolean enqueueMessage(Message msg, long when) {
    ...
    synchronized (this) {
        ...
        //標記 msg 被使用中
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // Inserted within the middle of the queue.  Usually we don't have to wake
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
           //找到鏈表適合的插入位置
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            //將鏈表插入到指定的位置
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }
        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

4.2 next()方法取出消息隊列中的消息

MessageQueue 中的 next() 方法是 Looper.loop() 中調用的。next() 是個死循環(huán)方法,直到消息隊列中有消息,返回該消息結束循環(huán)。這里為什么需要使用死循環(huán)呢?因為消息隊列中即便有消息,但是由于每一個 Message 有一個 when 屬性,只有達到的 Message 的 delay 時間才會去執(zhí)行當前消息,所以得通過死循環(huán)的方式知道該消息能夠去執(zhí)行才取出這個消息。

Message next() {
    ...
    for (;;) {//死循環(huán) 輪詢
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message. 獲取到一個消息 直接返回,然后將消息從消息隊列中移除
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }
            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }
            // If first time idle, then get the number of idlers to run.
            // Idle handles only run if the queue is empty or if the first message
            // in the queue (possibly a barrier) is due to be handled in the future.
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
            }
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        // Run the idle handlers.
        // We only ever reach this code block during the first iteration.
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the handler
            boolean keep = false;
            try {
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }
            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
        // Reset the idle handler count to 0 so we do not run them again.
        pendingIdleHandlerCount = 0;
        // While calling an idle handler, a new message could have been delivered
        // so go back and look again for a pending message without waiting.
        nextPollTimeoutMillis = 0;
    }
}

5. Looper 的工作原理

中文名叫輪訓器,Looper 開啟輪訓之后,這個過程是一個死循環(huán)的過程,當消息隊列中有消息時,輪訓器會取出這個消息交給 Handler 去處理。在 Handler 創(chuàng)建時,會默認使用當前線程的 Looper 作為輪訓器,那么它是如何去獲取當前線程的 Looper對象呢?這里涉及到另外一個知識點,那就是 ThreadLocal 概念,它不是一個線程,它可以在不同線程中互不干擾的獲取或者存儲數(shù)據(jù),可以通過 ThreadLocal 獲取不同線程中對應的 Looper,但是線程默認情況是沒有 Looper 的,需要使用Handler 就必須為當前線程創(chuàng)建一個 Looper 對象。而在主線程創(chuàng)建 Handler 的時候會默認使用主線程的 Looper,因為在 ThreadActivity中 main 方法已經(jīng)創(chuàng)建好了 Looper 對象,所以在主線程使用 Handler 是不需要手動創(chuàng)建 Looper 的。

5.1 準備 Looper 對象

//準備一個 Looper 對象。
Looper.prepare();

//源碼
private Looper(boolean quitAllowed) {
    //創(chuàng)建 Looper 的同時,也會創(chuàng)建一個 MessageQueue 對象
    //每一個 Looper 都會對應一個 MessageQueue 隊列
    mQueue = new MessageQueue(quitAllowed);
    //每一個 Looper 都會對應的 一個線程
    mThread = Thread.currentThread();
}

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

5.2 開啟輪訓操作

這個方法是阻塞式的,死循環(huán)的從 Looper 綁定的隊列中去獲取消息(queue.next()),直到消息隊列停止。當獲取到消息之后,直接將獲取到消息分發(fā)出去,具體分發(fā)過程待會再分析。

//開始循環(huán)
Looper.loop();

//源碼
public static void loop() {
    //判斷是否已經(jīng)創(chuà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;
    ...
    for (;;) {//阻塞式
        Message msg = queue.next(); // might block 在隊列中獲取消息
        if (msg == null) {// 消息隊列停止,沒有更多消息
            // No message indicates that the message queue is quitting.
            return;
        }
        ...
        //將輪訓到的消息進行分發(fā),將任務切換到 Handler 所在的線程中去執(zhí)行。
        msg.target.dispatchMessage(msg);
        ...
        msg.recycleUnchecked();//回收這個msg
    }
}

5.3 MainLooper 的創(chuàng)建。

因為在 ActivityThread#main 方法執(zhí)行了以上兩段代碼,這就表示應用啟動時就將主線程的 Looper 創(chuàng)建完畢了。所以在主線程中直接創(chuàng)建Handler,不用指定 Looper 即可使用。

public static void main(String[] args) {
    Looper.prepareMainLooper();
    ...
    Looper.loop();
}
public static void prepareMainLooper() {
    prepare(false);//創(chuàng)建一個 Looper,當前的 Looper 是在主線程中調用的
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();//在主線程的 Looper 賦值。
    }
}

5.4 如何在子線程使用 Looper?

若是要在非主線程中去使用 Looper 就必須手動地去創(chuàng)建 Looper 對象。而創(chuàng)建 Looper 的方式是通過 Looper.prepare()實現(xiàn), 底層調用的是 Looper.prepare(boolean) 創(chuàng)建一個 Looper 對象,在方法中判斷 ThreadLocal 中是否保存了當前線程的 Looper 對象,沒有的話,則創(chuàng)建一個 Looper 對象并且保存到 ThreadLocal 中的,具體的 ThreadLocal 的知識點參考:
http://www.itdecent.cn/p/aef305d37152。 創(chuàng)建的同時會為 Looper 創(chuàng)建一個 MessageQueue 消息隊列。該方法的作用只是創(chuàng)建一個 Looper 對象,并且保存起來,此時輪訓器還沒有開啟循環(huán)操作。通過 Looper.loop() 開啟消息輪訓操作。

new Thread(){
    public void run() {
        Looper.prepare();
        Handler handler = new Handler();//handler 使用的就是上面創(chuàng)建的 Looper
        Looper.loop();
    }
}.start();

5.5 停止消息輪訓操作

Looper 中提供了兩個方法去停止輪訓操作,quit()的方法注釋中可以看出該方法是unsafe的,因為quit()被調用之后,那么其他沒執(zhí)行完的消息就沒有辦法被執(zhí)行,而 quitSafe() 的注釋表示該方法是安全的,它會等到所有的消息執(zhí)行完畢之后采取停止輪訓操作。

/**
 * Quits the looper.
 * <p>
 * Causes the {@link #loop} method to terminate without processing any
 * more messages in the message queue.
 * </p><p>
 * Any attempt to post messages to the queue after the looper is asked to quit will fail.
 * For example, the {@link Handler#sendMessage(Message)} method will return false.
 * </p><p class="note">
 * Using this method may be unsafe because some messages may not be delivered
 * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
 * that all pending work is completed in an orderly manner.
 * </p>
 *
 * @see #quitSafely
 */
public void quit() {
    mQueue.quit(false);
}
/**
 * Quits the looper safely.
 * <p>
 * Causes the {@link #loop} method to terminate as soon as all remaining messages
 * in the message queue that are already due to be delivered have been handled.
 * However pending delayed messages with due times in the future will not be
 * delivered before the loop terminates.
 * </p><p>
 * Any attempt to post messages to the queue after the looper is asked to quit will fail.
 * For example, the {@link Handler#sendMessage(Message)} method will return false.
 * </p>
 */
public void quitSafely() {
    mQueue.quit(true);
}

6. Handler 的內部實現(xiàn)原理

Handler 的職責就是負責往消息隊列中發(fā)送和處理消息的。對于發(fā)送消息而言, Handler 中有一系列的 postXxx 和 sendXxx 方法。對于處理消息 Hadnler 是通過 handlerMessage 去處理的。

6.1 通過 sendMessage 的方式往 MessageQueue 插入消息

public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;//looper綁定的消息隊列
    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);
}
//sendMessageDelayed 最終會調用 enqueueMessage方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;//指定message的target為當前handler對象
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);//交給 MessageQueue去處理
}

6.2 通過 post 的方式往 MessageQueue 插入消息

public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}
//將一個Runnable轉化為一個 Message 對象
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;//保存到callback屬性的,之后分發(fā)時需要拿其做判斷使用
    return m;
}

不管是 post ,postDelay 還是sendMessage,sendMessageDelayed的方式去發(fā)送消息,底層都是調用 sendMessageDelayed 去實現(xiàn)。從 getPostMessage 可以知道其實發(fā)送一個 Runnable 對象實際上也是將其轉化為一個 Message 對象去發(fā)送的。sendMessageDelayed 最終會調用 enqueueMessage方法,在這個方法 msg 做一些賦值操作,例如給 msg 綁定一個target,就是說當前這個需要發(fā)送的消息它有由這個 Handler 發(fā)出的,設置 target 的原因就是方便待會 MessageQueue 知道具體分發(fā)給哪個一個Hadnler 去處理這個消息。而代碼最終會走到queue.enqueueMessage(msg, uptimeMillis);這個queue是在哪里賦值的呢?代碼回到Handler的構造中可以看出,其實 queue 就是 Looper 中綁定的 MessageQueue 對象,在 Handler 中也保存了一份引用。

6.3 處理消息

消息是通過 輪訓器輪訓出來的,也就是通過 loop 中去不斷的調用 messagequeue中的next 方法獲取一條 Message 消息,代碼回到4.2小節(jié)中,獲取到的 Message,然后調用 msg.target.dispatchMessage(msg); 將輪訓到的消息進行分發(fā),將任務切換到 Handler 所在的線程中去執(zhí)行。這里的msg.target 就是發(fā)送消息的 Handler 對象。

6.3.1 handler 是如何通過 dispatchMessage 去處理不同的消息的?

判斷 msg.callback 是否為 null,這個 callback 剛才在 post 方式發(fā)送一個 Runnable 對象時,通過 getPostMessage(Runnable) 轉化時,將 Runnable 作為 Message 的 callback 屬性。也就是說若是 msg.callback 不會null,表示這個消息就是通過 post 方式實現(xiàn)的。若為null,分為兩種方式,一是通過通過具有 Callback 參數(shù)來創(chuàng)建的 Handler,那么就會會調用 Callback#handleMessage(),二以默認的方式來創(chuàng)建的Handler,因此會調用 Handler#handleMessage()方法。具體哪種方式處理消息,用戶只需要去覆寫對應的handleMessahe 即可。好了,這就是我對 Handler 消息處理機制的理解。

//根據(jù)不同的處理方式分發(fā)這個消息
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {//第一種方式:post 方式
        handleCallback(msg);
    } else {
        if (mCallback != null) { //第二種方式:通過具有 Callback 參數(shù)來創(chuàng)建的 Handler
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);//第三種方式:創(chuàng)建Handler時沒有指定 Callback 屬性。
    }
}
//Handler 內部的一個接口
public interface Callback {
    public boolean handleMessage(Message msg);
}
//默認的構造,沒有給callback賦值
public Handler() {
    this(null, false);
}

//具有 Callback 的構造來創(chuàng)建 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;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容