Handler消息機制

先舉個子線程中使用Handler的例子:

package com.haoran.myhandler;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
    Looper looper;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(){
            @Override
            public void run() {
                System.out.println("子線程中打印線程名:"+Thread.currentThread().getName());
                Looper.prepare();
                looper = Looper.myLooper();
                looper.loop();

            }
        }.start();
        Handler handler = new Handler(looper){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                System.out.println("主線程中打印線程名:"+Thread.currentThread().getName());
            }
        };
        Message message = Message.obtain();
        handler.sendMessage(message);
    }
}

大家看了這段代碼估計會有疑惑,子線程中怎麼沒有new Looper()? Handler(looper)的構造做了什麼操作?

那好,我們debug運行下
這裏寫圖片描述

其實
從 FrameWork層 MainActivity.class –> Activity.class –> ActivityThread.class –> ActivityThread.class中的內部類(H.class) 和main方法。
這裏寫圖片描述
Handler消息機制起了很大的作用,從四大組件的啓動,模板模式進行生命週期方法的回調,都是消息機制在默默的進行着。從源碼層看,安卓主線程在創建過程中,在ActivityManager的main入口,通過自動初始化了一個Looper和Handler以及handleMessage方法進行消息機制 再通過H.class中進行四大組件的消息機制的判斷
消息類型定義:
消息類型
消息處理方法:
消息處理
我們說了半天的Handler消息機制的重要性好像和Handler消息機制原理沒有半毛錢關係,最開始代碼引發我們思考的問題依然無法回答。

下面我們從Handler消息機制的UML圖,及相關類源碼進一步進行分析,先上圖:
這裏寫圖片描述

這張架構圖我們可以花時間分型一下,分析他們之間互相持有的關係。哈哈,這裏我就簡單概述一下,再走一下源碼。。。

首先
作爲一個合格的助手,Handler裏有mQueue和mLooper對象;於是就有了消息隊列MessageQueue和輪詢器Looper,再將消息Message放在消息隊列裏面。Message記錄着相對應的Handler,MessageQueue裏持有Message對象,Looper通過持有的MessageQueue對象,對消息進行輪詢讀寫

圖解:
這裏寫圖片描述

上圖的代碼大家應該看着不舒服,我把源碼截取一點,一起走一遍,不過我們要帶着問題走:
1. 消息怎麼被髮送到消息隊列中的?
2. 消息怎麼被Handler/Looper處理的?
3. Handler 對象是怎麼創建出來的?
4. loop 這個方法什麼時候執行?
5. Message 、Handler 、 Looper、 MessageQueue 、 Thread、 ThreadLocal直接的關係是什麼呢?

我們知道,
handler.sendMessage(Messagemessage)handler.postRunnable(Runnable runnable);

都是可以發送消息的,其實post底層也是封裝了sendMessage方法的,所以我們從handler.sendMessage(Message msg)開始吧 –>go

Mainactivity中 handler.sendMessage(message)

 Message message = Message.obtain();
 handler.sendMessage(message);

點進去 –>Handle.class的sendMessage方法

sendMessage
繼續 –> sendMessageDelayed方法
sendMessageDelayed
繼續 –> sendMessageAtTime方法
sendMessageAtTime

這裏發現mQueue,按谷歌工程師開發習慣mQueue是Handler.class類裏面的成員變量。那麼,mQueue是在什麼時候被賦值的呢?
我們找一找 –> 發現Handler(Callback callback, boolean async)構造
構造
Handler的其他構造裏,同樣賦值操作是Looper給的,那麼mLooper是如何賦值的呢?

可以通過handler構造外界傳遞一個,通常我們用無參構造。哈哈,我們的例子裏傳遞了一個looper哦,大家還記得嗎?
上圖裏 mLooper = Looper.myLooper();

那麼Looper裏的myLooper()方法做了什麼操作呢?點點點! –>
myLooper()方法

??sThreadLocal 又是什麼啊?這裏留個坑,一會兒填,咱們先刻舟求劍

好了,先回到 sendMessageAtTime方法發現,走了自己的enqueueMessage();方法,–>Handler.class 的enqueueMessage()
這裏寫圖片描述

發現 –>

走到了MessageQueue.class 的enqueueMessage(Message msg, long when) 方法
排隊1
排隊2
排隊3

這個方法有點長用僞代碼解釋一下吧

if (p == null || when == 0 || when < p.when) {
 // 當前發送的message需要馬上被處理調,needWake喚醒狀態置true
 } else {
 // 當前發送的message被排隊到其他message的後面,needWake喚醒狀態置false
 }
 .....
 // 是否喚醒主線程
 if (needWake) {
     nativeWake(mPtr);
 }

MessageQueue.enqueueMessage()執行,會往消息隊列裏添加Message。消息隊列MessageQueue按照時間對所有的Message進行排序。
往消息隊裏裏添加Message分爲兩種情況
1.當前發送的Message位於隊列頭部。需要馬上處理消息。就會把當前的喚醒狀態needWake喚醒狀態置爲true,喚醒主線程,讓輪循器取消息。
2.當前發送的Message被排隊排到了其他Message的後面。就不需要喚醒主線程了。就會把當前的喚醒狀態needWake置爲false。不喚醒主線程。

等等,哈哈,我們的Handler.class中enqueueMessage方法還沒有分析完畢,細心的同學會發現 – >
msg.target = this;
這做了什麼操作?
//1. Message的target標記爲當前發送的Handler
//2. Looper取到message根據target把message分發給相應的Handler

至此,我們把Handler的發送,MessageQueue的給消息排隊解釋完畢了,接下來就是Looper中如何輪詢獲取消息,並交給Handler處理的

看Looper.class –> 核心Looper.loop()輪循
looper1
這裏寫圖片描述

 while (true) {//死循環,輪循取消息
    // 取消息,如果沒有消息,就阻塞
    Message msg = queue.next(); // might block //有可能會阻塞。
     ...
    //取消息。取到消息就發送給Handler
    msg.target.dispatchMessage(msg);
      ...
 }

queue.next()方法

 Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            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;
        }
    }

msg.target.dispatchMessage(msg)也就是Handler.dispatchMessage方法

/**
     * 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);
        }
    }
dispatchMessage: 先判斷 Message 對象上的 callback 是否爲空,
     如果不爲空,執行 callback 上的 run 方法(聯想到了handler.postRunnable方法了嗎?)
     如果爲空,讓 handler 去處理消息,判斷 handler 上的 mCallback 是否爲空
    ①如果 mCallback 不爲空,讓 mCallback.handleMessage 方法來處理消息
    ②如果 mCallback 爲空,纔會調用 handler.handleMessage 方法來處理消息

哈哈,差不多了,還記得我們留的一個坑嗎ThreadLocal看源碼吧:)
這裏寫圖片描述

很像一個map集合啊,就當是一個hashmap集合吧,我們android中把 K設置爲thread名,V設置爲looper吧

get方法 :


public T get() {
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values != null) {
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            values = initializeValues(currentThread);
        }

        return (T) values.getAfterMiss(this);
    }

set方法 :

public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }

哈哈,再見了,不足的地方咱們一起學習吧

發佈了28 篇原創文章 · 獲贊 22 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章