目錄
- 簡(jiǎn)介
- ThreadLocal
- MessageQueue
- Looper
- Handler
簡(jiǎn)介
消息處理機(jī)制應(yīng)該說基本都用過,因?yàn)锳ndroid中不允許在UI線程中進(jìn)行一些耗時(shí)的操作悼粮,否則就會(huì)出現(xiàn)ANR略吨,而將耗時(shí)操作放在子線程中運(yùn)行,運(yùn)行結(jié)束后捞稿,子線程需要返回?cái)?shù)據(jù)給UI線程,但是子線程中是不能直接更新UI線程的數(shù)據(jù)的,因此就需要子線程將數(shù)據(jù)法統(tǒng)給UI線程葫哗,然后UI線程進(jìn)行更新,也就是我們一直使用的Handler球涛。
整體的工作流程:在一個(gè)線程創(chuàng)建一個(gè)Handler劣针,在另一個(gè)線程線程中通過Handler將一個(gè)Message 發(fā)送到創(chuàng)建這個(gè)Handler的線程進(jìn)行處理。
這中間有幾點(diǎn)需要注意:
1.創(chuàng)建Handler的線程必須要有一個(gè)Looper亿扁,否則就會(huì)報(bào)錯(cuò)捺典,我們平時(shí)在UI線程創(chuàng)建的時(shí)候UI線程自己已經(jīng)創(chuàng)建好了Looper,因此我們感覺不到从祝。
2.在調(diào)用了Handler的send方法之后襟己,其實(shí)是Handler將消息放到了待處理消息的線程的消息隊(duì)列中,即MessageQueue牍陌。每個(gè)線程中的Looper都維護(hù)著一個(gè)消息隊(duì)列擎浴,且一個(gè)線程中只有一個(gè),只能本線程訪問毒涧。
3.線程中的Looper無限循環(huán)的讀取消息隊(duì)列贮预,有消息來時(shí)就拿出來進(jìn)行處理,沒有就阻塞契讲。
簡(jiǎn)單的說仿吞,線程A創(chuàng)建了Handler,線程B用這個(gè)Handler用來發(fā)送消息到A的消息隊(duì)列捡偏,A的Looper不斷的循環(huán)取消息唤冈,取出來后A中的Handler進(jìn)行相應(yīng)的操作。
消息處理機(jī)制主要就是這四部分組成:Handler银伟、Looper你虹、MessageQueue、Message彤避,大致了解了工作流程中之后傅物,下來就分別學(xué)習(xí)一下每個(gè)模塊的和原理。
ThreadLocal
這個(gè)有必要提一下忠藤。
這個(gè)類主要是用來存儲(chǔ)線程內(nèi)部的數(shù)據(jù)的挟伙。通過它可以使得線程存儲(chǔ)數(shù)據(jù),而且只有自己才可以獲取到數(shù)據(jù),其他線程無法獲取尖阔,即使不同的線程訪問的是同一個(gè)ThreadLocal對(duì)象贮缅。前面提到的Handler在創(chuàng)建的時(shí)候就會(huì)使用當(dāng)前線程的Looper構(gòu)造消息循環(huán)系統(tǒng),那么Handler在創(chuàng)建的時(shí)候獲取當(dāng)前線程的Looper就是通過ThreadLocal介却。使用:通過構(gòu)造ThreadLocal對(duì)象谴供,然后調(diào)用set與get方法即可。
set實(shí)現(xiàn)原理:
ThreadLocal有一個(gè)靜態(tài)內(nèi)部類ThreadLocalMap齿坷,這個(gè)Map的鍵就是ThreadLocal桂肌,值則是我們set進(jìn)去的一個(gè)Object。
這個(gè)Map在創(chuàng)建的時(shí)候是根據(jù)當(dāng)前線程來創(chuàng)建的永淌。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
在set的時(shí)候如果當(dāng)前線程中不存在Map則進(jìn)行創(chuàng)建崎场,如果已經(jīng)存在則將值set進(jìn)去。
大致的set過程是這樣的:ThreadLocalMap中有一個(gè)Entry(鍵是ThreadLocal遂蛀,值是傳入的Object)數(shù)組谭跨,根據(jù)傳入的鍵(ThreadLocal)計(jì)算出一個(gè)hash值,映射到數(shù)組index上李滴,將ThreadLocal作為鍵value作為值的一個(gè)Entry存到數(shù)組的index+1上螃宙。
有點(diǎn)繞,梳理一下所坯,當(dāng)每個(gè)Thread都用的是一個(gè)ThreadLocal谆扎,然后根據(jù)ThreadLocal為每一個(gè)線程都創(chuàng)建一個(gè)Map,這個(gè)map中存放的是ThreadLocal以及set進(jìn)去的值芹助。
然后是get方法:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
根據(jù)線程獲取到map堂湖,其中有一個(gè)數(shù)組,然后根據(jù)ThreadLocal獲取到數(shù)組中的Entry周瞎,再獲取到其中的值苗缩。
MessageQueue
消息隊(duì)列,主要用于放入消息和取出消息声诸,其中實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu)其實(shí)是單鏈表。主要方法有兩個(gè):boolean enqueueMessage(Message msg, long when)
和Message next()
退盯,分別對(duì)應(yīng)的是入隊(duì)和出隊(duì)的操作彼乌。
在出隊(duì)操作中有一個(gè)無限循環(huán),如果沒有消息渊迁,則Looper調(diào)用next就會(huì)阻塞在這里慰照,一直循環(huán)。
Looper
主要扮演的角色就是一直循環(huán)從隊(duì)列里拿消息琉朽。
創(chuàng)建Handler的時(shí)候需要該線程有Looper毒租,創(chuàng)建Looper的過程:
//創(chuàng)建一個(gè)Looper
Looper.prepare();
//開啟無限循環(huán)
Looper.loop();
另外,如果是自己在子線程中創(chuàng)建的Looper箱叁,在使用完畢之后一定要關(guān)掉墅垮,否則將會(huì)無限循環(huán)下去惕医。
關(guān)閉的方式:在Looper中有以下兩個(gè)方法:
public void quitSafely() {
mQueue.quit(true);
}
public void quit() {
mQueue.quit(false);
}
簡(jiǎn)單的說就是quit是直接退出,quitSafely則是在隊(duì)列中的消息都處理完之后退出算色。
loop方法:
這個(gè)方法是一個(gè)無限循環(huán)方法抬伺,唯一的出口就是當(dāng)隊(duì)列的next返回了null,也就是在調(diào)用了mQueue.quit方法之后才會(huì)退出灾梦。
然后看一下Looper取出消息之后干了啥:
msg.target.dispatchMessage(msg);
這個(gè)msg.target就很容易了峡钓,就是前面創(chuàng)建的Handler。
也就是說若河,取出消息之后能岩,交給了Handler來進(jìn)行處理。
Handler
首先是Handler的send
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);
}
可以看到萧福,就是將消息入隊(duì)了拉鹃。
前面提到,在Looper取出消息的時(shí)候统锤,就會(huì)交給Handler來處理:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
終于毛俏,看到我們熟悉的handleMessage(msg);
了。
那這兩個(gè)callback又是怎么回事饲窿?
msg.callback這個(gè)是一個(gè)Runnable對(duì)象煌寇,也就是Handler的傳遞的參數(shù)。
mCallback:
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
可以通過這個(gè)Callback 來創(chuàng)建一個(gè)不需要派生子類的Handler逾雄,也就是另外一種創(chuàng)建Handler的方式阀溶,一般我們都是繼承Handler復(fù)寫handleMessage。
還有一點(diǎn)鸦泳,就是創(chuàng)建一個(gè)Handler可以通過傳遞一個(gè)Looper來實(shí)現(xiàn)银锻,默認(rèn)創(chuàng)建的時(shí)候,我們是獲取創(chuàng)建線程的Looper做鹰,而這種創(chuàng)建方式就指定特定的Looper击纬,這樣的話就可以實(shí)現(xiàn)不同的線程中創(chuàng)建的Handler共用同一個(gè)Looper,也就是共用消息隊(duì)列钾麸。
主要的核心源碼都沒有貼更振,只是大致了解了其實(shí)現(xiàn)方式和流程,一方面是篇幅太長(zhǎng)了饭尝,另外主要就是水平不是特別夠肯腕,也就了解個(gè)大概,后面還需要繼續(xù)深入學(xué)習(xí)钥平。