Android消息機(jī)制主要是指Handler的運(yùn)行機(jī)制,以及MessageQueue和Looper的工作過(guò)程驼修。
概述
從開(kāi)發(fā)的角度來(lái)說(shuō)诈铛,Handler是Android消息機(jī)制的上層接口,開(kāi)發(fā)中只需要關(guān)心Handler的交互即可耳峦。
很多人認(rèn)為Handler的作用是更新UI焕毫,這個(gè)認(rèn)為沒(méi)有錯(cuò)但是比較片面,它只是Handler的一種使用場(chǎng)景循签。
Handler的主要作用是將一個(gè)任務(wù)切換到某一個(gè)指定的線程中去執(zhí)行疙咸。
Handler的運(yùn)行需要底層的MessageQueue和Looper的支撐。
MessageQueue叫做消息隊(duì)列乞旦,是用鏈表來(lái)實(shí)現(xiàn)的一個(gè)存儲(chǔ)消息的列表腔召。
Looper是混喚起扮惦,會(huì)以無(wú)限循環(huán)的形式查找消息。線程默認(rèn)沒(méi)有Looper浊仆,需要手動(dòng)為其創(chuàng)建,但是主線程即UI線程舔琅,會(huì)在創(chuàng)建的時(shí)候自動(dòng)初始化一個(gè)Looper洲劣。
ThreadLocal是Looper中的一個(gè)特殊概念,它不是線程而是一種數(shù)據(jù)載體囱稽,它可以互不干擾的為每一個(gè)線程存儲(chǔ)/提供數(shù)據(jù)。
由于Android的UI控件是線程不安全的流昏,多線程并發(fā)訪問(wèn)會(huì)導(dǎo)致控件處于不可預(yù)計(jì)的狀態(tài)吞获,但若加線程鎖,又會(huì)導(dǎo)致其訪問(wèn)邏輯變得復(fù)雜并且低效刁绒,得不償失烤黍。所以Android在設(shè)計(jì)上,規(guī)定訪問(wèn)UI線程只能在主線程中進(jìn)行初狰,并且在ViewRootImpl接口中對(duì)UI操作做了驗(yàn)證互例。
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(...);
}
}
并且提供了Handler機(jī)制,解決在子線程中訪問(wèn)UI線程的問(wèn)題腥光。
Handler的使用此處不表糊秆,這次的重點(diǎn)是Handler以及其底層類的運(yùn)作原理。
MessageQueue
MessageQueue捉片,即消息隊(duì)列,內(nèi)部通過(guò)一個(gè)單鏈表的數(shù)據(jù)結(jié)構(gòu)來(lái)維護(hù)消息列表伍纫,使用單鏈表而非隊(duì)列是因?yàn)閱捂湵碓诓迦胍约皠h除操作上比較有優(yōu)勢(shì)。
消息隊(duì)列主要有兩個(gè)操作赔蒲,插入和刪除(讀攘际)。
enqueueMessage是插入方法母市,向隊(duì)列中插入一條消息
next是刪除方法,從消息隊(duì)列中取出一條消息焕刮,并將其從隊(duì)列中刪除墙杯。并且next方法是一個(gè)無(wú)限循環(huán)的方法,如果消息隊(duì)列中沒(méi)有消息溉旋,那么next方法會(huì)被阻塞嫉髓,直到有新消息出現(xiàn)。
ThreadLocal
介紹Looper之前必須先介紹ThreadLocal這個(gè)線程內(nèi)部的數(shù)據(jù)存儲(chǔ)類梧油。
ThreadLocal也叫線程本地變量州邢,它的特性是,它可以互不干擾的為每一個(gè)線程存儲(chǔ)/提供數(shù)據(jù)骗村,使用的時(shí)候會(huì)對(duì)線程自身的內(nèi)存進(jìn)行操作呀枢,并且提供了get/set方法來(lái)訪問(wèn)。簡(jiǎn)單來(lái)說(shuō)ThreadLocal可以為不同的線程提供不同的數(shù)據(jù)副本琅拌。
白話有點(diǎn)抽象,舉個(gè)例子:
private ThreadLocal<String> stringThreadLocal;
stringThreadLocal = new ThreadLocal<>();
stringThreadLocal.set("main");
Log.e(TAG, "run: " + stringThreadLocal.get());
new Thread(new Runnable() {
@Override
public void run() {
stringThreadLocal.set("Thread1");
Log.e(TAG, "run: " + stringThreadLocal.get());
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
Log.e(TAG, "run: " + stringThreadLocal.get());
}
}).start();
12-26 16:25:57.208 19506-19506/com.zx.studyapp E/WindowActivity: run: main
12-26 16:25:57.209 19506-19800/com.zx.studyapp E/WindowActivity: run: Thread1
12-26 16:25:57.210 19506-19801/com.zx.studyapp E/WindowActivity: run: null
ThreadLocal底層也是用Map實(shí)現(xiàn)财忽,可以近似的理解為即彪,此Map的key是線程標(biāo)識(shí)活尊,value就是當(dāng)前線程所對(duì)應(yīng)的值。
ThreadLocal的這個(gè)特性深胳,正好滿足Handler中Looper的需求:Looper的作用域是當(dāng)前線程铜犬,并且不同的線程擁有不同的Looper。
Looper
Looper 就是循環(huán)器癣猾,它會(huì)以無(wú)限循環(huán)的形式,向MessageQueue中查找新消息夸盟,有新消息就立即處理像捶,否則就阻塞。
Handler工作的時(shí)候需要Looper循環(huán)器的參與释簿,但是除了主線程硼莽,其他線程不會(huì)默認(rèn)初始化一個(gè)Looper供我們使用,這時(shí)候就需要手動(dòng)創(chuàng)建一個(gè)Looper渐尿。
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();//創(chuàng)建一個(gè)Looper
Handler handler = new Handler();
Looper.loop();//開(kāi)啟消息循環(huán)
}
}).start();
Looper的loop方法中有一個(gè)死循環(huán)矾瑰,用來(lái)讀取MessageQueue中的新消息。
Looper除了prepare方法之外凉夯,還提供了prepareMainLooper方法為主線程創(chuàng)建Looper使用,本質(zhì)上還是prepare方法劲够。
此外,Looper還提供了兩個(gè)方法來(lái)退出消息循環(huán)征绎,分別是quit和quitSafely,他們的區(qū)別是:
quit會(huì)直接退出Looper柴墩;
quitSafely會(huì)設(shè)定一個(gè)標(biāo)記凫岖,當(dāng)隊(duì)列中已有的消息全部處理完成后,才會(huì)退出歼指。
Looper退出后甥雕,Handler發(fā)送的消息就會(huì)失敗,即send方法返回false犀农。在子線程中,應(yīng)該在所有任務(wù)處理完成后立即調(diào)用quit方法來(lái)退出消息循環(huán)赁濒,否則這個(gè)子線程就會(huì)持續(xù)處于等待狀態(tài)孟害,無(wú)法終止。
Handler
Handler主要工作是發(fā)送和接受消息击你。
Handler有一個(gè)特殊的構(gòu)造方法谎柄,是通過(guò)特定的Looper來(lái)構(gòu)造,這個(gè)方法會(huì)在默認(rèn)構(gòu)造方法中被調(diào)用朝巫,所以如果當(dāng)前線程沒(méi)有Looper的話,就會(huì)拋出無(wú)法創(chuàng)建Handler的異常拙吉。
Handler發(fā)送主要通過(guò)post或者send方法來(lái)實(shí)現(xiàn)(最終都是通過(guò)send實(shí)現(xiàn))。Handler發(fā)送消息的過(guò)程筷黔,實(shí)質(zhì)上就是向消息隊(duì)列中插入一條數(shù)據(jù)。
Handler.send在子線程發(fā)送了一條消息椎例,之后Looper的loop方法不再阻塞名眉,通過(guò)MessageQueue.next獲取了新消息凰棉,接著通過(guò)msg.targer.dispatchMessage方法,將其傳遞給Looper所在線程的Handlder撒犀,完成了一次消息傳遞福压。
如下圖: