手把手帶你從源碼的角度全面理解Handler、Looper、MessageQueue之間的關係

目標:
首先要明確幾個目標,我們要解決以下幾個問題:
a.線程是如何與Looper關聯起來的?
b.一個線程裏面可以有幾個Looper?
c.消息是怎麼從一個線程傳遞到另一個線程的?
d.Handler、Looper、MessageQueue三者之間的關係是怎樣的?

分析源碼前,先舉個栗子:

MessageQueue相當於一個池塘,Message就是池塘裏面的水,Looper就是一臺抽水機,現在我們要用這臺抽水機把池塘裏的水抽出來,但是首先我們這個池塘開始是沒有水的,Handler就相當於一個人,人首先會往池塘裏放水(調用handler.sendMessage()等方法),然後還要記得的是,要給我們的抽水機Looper插上電啊,沒電你怎麼抽水?抽電就是調用Looper.loop()方法,水從池塘抽出來後,又會交給人Handler去處理(通過handler的handleMessage)。

下面我們就開始分析源碼吧:
通常我們使用Handler來做Android線程間通信都會在new一個Handler對象,那麼我們便從Handler的構造方法看起
1、

public Handler() {
    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 = null;
}

我們可以看到上面的
mLooper = Looper.myLooper();方法
那麼我們進入這個方法一探究竟
2、

public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
它只是返回sThreadLocal的get()方法後強轉成了Looper對象。
那麼sThreadLocal又是個是啥呢?咱先不去管它。咱先去看看它的get()方法。代碼如下:
3、

@SuppressWarnings("unchecked")
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);
}

可以看到它首先調用了Thread currentThread = Thread.currentThread();獲取到了當前線程,這個大家都很熟悉,就是說我們在哪個線程裏面new Handler();拿到的自然也就是哪個線程。然後下面一行代碼又調用Values values = values(currentThread);方法,傳入當前線程對象獲得了一個Values對象,那麼這個Values又是個啥呢?它是ThreadLocal的一個靜態內部類,Values其實就是Map,它的實現方式基本和HashMap一樣(這個大家可以自己去仔細閱讀源碼,我這裏就不貼出來了,代碼有些長),那麼我們繼續看這個values(Thread)方法.

Values values(Thread current) {
return current.localValues;
}
這個方法是ThreadLocal的一個方法,它的代碼很簡單,只有一行,返回了傳入線程對象的localValues屬性,而這個Thread的localValues屬性在Thread類裏面是這樣聲明的:

ThreadLocal.Values localValues;
可以看出它其實就是一個Values對象,其實就是一個Map集合,而且在Thread類裏面並沒有對它進行初始化。那麼我們回到上面標號爲3的代碼處的Values values = values(currentThread)這句代碼,現在我們便知道了原來這句代碼是返回了當前線程currentThread的一個屬性localValues,而這個屬性就是一個Map集合。
然後繼續往下看get方法裏的這段代碼

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);
}
之前我們也說了Thread類裏面localValues並沒有初始化,也就是我們拿到的values對象是爲null的,那麼理所當然它走的是else代碼塊,可以看到裏面對values進行了賦值。很簡單一行代碼,給values賦值。

Values initializeValues(Thread current) {
return current.localValues = new Values();
}
最後get()方法的返回值(T) values.getAfterMiss(this);之前說了,values其實是一個HashMap,它的getAfterMiss其實就是和HashMap的get方法一樣,只是換了個名字罷了。閱讀源碼其實可以發現Values它是以ThreadLocal對象爲鍵,Looper對象爲值的,所以ThreadLocal和Looper是一一對應的,而ThreadLocal又是和當前線程一一對應,所有Looper也和當前線程一一對應了。因爲是ThreadLocal裏面調用這個方法,所以在values.getAfterMiss(this)方法傳的這個this就是ThreadLocal對象,取出與一一對應的Looper返回。
最後回到標號爲 2的代碼, return (Looper)sThreadLocal.get();這句就是返回了與sThreadLocal一一對應的Looper對象。

然後我們再看到標號爲1的那段代碼,在 mLooper = Looper.myLooper();調用後會有下面這個判斷:

    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }

就是說當前mLooper爲空就會報異常,必須先創建一個Looper對象才行。其實在主線程中已經默認爲我們創建了這個Looper對象(這個可以在ActivityThread的main方法中看到)。但是在如果你想要在子線程中new一個Handler去處理消息前,就必須去調用Looper.prepare() 去創建一個Looper。代碼如下:

public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException(“Only one Looper may be created per thread”);
}
sThreadLocal.set(new Looper());
}
可以看到prepare方法裏面首先就做了一個判斷sThreadLocal.get()是否爲空,這個方法我們已經分析過了,它返回的是當前線程(new Handler的那個線程)對應的Looper對象,如果不爲空,就會拋異常,也就證明一個線程裏面只能有一個Looper,創建兩次或多次就會出錯,如果爲空,會看到它又調用了ThreadLocal的set方法,並把new出來的Looper作爲參數傳了進去。set方法具體代碼如下:

public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
之前我們分析過ThreadLocal的get方法,那麼它的set方法也是差不多的,最後那句代碼以當前ThreadLocal對象爲鍵,value(也就是我們的Looper對象)爲值存入到values這個map中。

下面我們再來分析MessageQueue:
首先看一段在線程中new Hnadler的常見代碼:

new Thread(new Runnable() {
public void run() {
Looper.prepare();
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
……
}
};
handler.sendEmptyMessage(1);
Looper.loop();
};
}).start();
Looper.prepare之前我們已經說到了,主要是下面的Looper.loop()方法,我們進入這個方法瞧瞧:

public static final void loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;//第3行代碼
while (true) {
Message msg = queue.next(); // might block
//if (!me.mRun) {
// break;
//}
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
if (me.mLogging!= null) me.mLogging.println(
“>>>>> Dispatching to ” + msg.target + ” ”
+ msg.callback + “: ” + msg.what
);
msg.target.dispatchMessage(msg);//第18行代碼
if (me.mLogging!= null) me.mLogging.println(
“<<<<< Finished to ” + msg.target + ” ”
+ msg.callback);
msg.recycle();
}
}
}
第3行代碼獲得了1個當前Looper的MessageQueue對象,那麼我們再去看看me.mQueue是在什麼時候初始化的,代碼如下:

private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
從上面這段代碼看出,它是在Looper的構造方法裏面就給創建了。還記得不,Looper.prepare()方法最後那行代碼sThreadLocal.set(new Looper()),也就是說在Looper.prepare()調用的時候我們的 MessageQueue就已經創建了。
然後我們繼續看loop方法,裏面用while(true)開啓了一個死循環,不斷的從MessageQueue區Message進行處理。

再看到loop方法的第18行代碼 調用了msg.target.dispatchMessage(msg),那麼msg.target的又是個什麼鬼?從意思上知道它是”目標”,進入到Message裏面看可以知道它就是一個Handler。我們再調用handler.sendEmptyMessage(1); 發消息的時候其實最終會調用Handler的sendMessageAtTime(Message msg, long uptimeMillis)方法(可自行看源碼,爲了簡便,中間省了一些過程),代碼如下:

public boolean sendMessageAtTime(Message msg, long uptimeMillis){
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;//第5行代碼
sent = queue.enqueueMessage(msg, uptimeMillis);//第6行代碼
}
else {
RuntimeException e = new RuntimeException(
this + ” sendMessageAtTime() called with no mQueue”);
Log.w(“Looper”, e.getMessage(), e);
}
return sent;
}
看到這段代碼的第5行, msg.target = this; msg.target 賦了值,值就是當前的Handler。再看到第6行代碼,它將我們發送的消息加入了消息隊列MessageQueue。那麼一切就解釋的通了。

handler發送消息就是將消息加入消息隊列裏面去,然後Loop.loop()方法將消息隊列裏面的消息不斷的取出來進行處理,進行處理的方法就是:msg.target.dispatchMessage(msg)方法,代碼如下:

public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看到最後哪行代碼handleMessage(msg),它最終還是回調
handler的handleMessage方法來處理消息的,那麼也就和我們的以前瞭解的不謀而合了。總體上可以用下面這張圖概括:(子線程發消息到主線程的處理圖)

這裏寫圖片描述
最後到這裏,我們的源碼就分析完畢了,但是最後要說的是還是要自己看一看源碼,博客寫的再詳細,也不可能有源碼詳細,全面。

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