要想深入理解Handler機(jī)制唯袄,就要理解:
- Android為何要引入Handler機(jī)制岔绸?
- Handler機(jī)制究竟是什么秦忿?
如果去查閱官方Handler文檔内颗,會(huì)發(fā)現(xiàn)官方?jīng)]有給Handler下過(guò)定義捻激,更沒(méi)有介紹為何要引入Handler機(jī)制制轰。官方文檔的第一句就說(shuō)Handler允許你發(fā)送和處理與一個(gè)線程的消息隊(duì)列關(guān)聯(lián)的消息和任務(wù)。這是對(duì)Handler能力和用法的一種描述胞谭,并沒(méi)有講為什么和是什么垃杖,這就造成初學(xué)者只知道怎么用Handler,但是不知道其背后的設(shè)計(jì)思想和原理丈屹。
Android為何引入Handler
要知道Android為何引入Handler機(jī)制调俘,首先要了解Java線程機(jī)制:
- 線程執(zhí)行任務(wù)
- 任務(wù)在線程的run方法中執(zhí)行
- run方法結(jié)束,線程終止
這種機(jī)制的問(wèn)題在于旺垒,創(chuàng)建線程的開(kāi)銷(xiāo)非常大彩库,即使通過(guò)線程池重用線程,線程調(diào)度的開(kāi)銷(xiāo)對(duì)于數(shù)量多先蒋、密度大骇钦、要求快速響應(yīng)的UI更新任務(wù)來(lái)說(shuō)也是不可接受的,于是Android引入Handler線程解決這個(gè)問(wèn)題竞漾。Handler線程就是Handler機(jī)制的核心:Handler線程永不退出眯搭,在run方法中輪詢(xún)?nèi)蝿?wù),有任務(wù)就處理业岁,沒(méi)任務(wù)就等待鳞仙。
有些Handler線程是允許退出的。
Android應(yīng)用的主線程是一個(gè)不允許退出叨襟、生命周期與進(jìn)程一樣長(zhǎng)的線程繁扎。
Handler機(jī)制工作原理
Handler機(jī)制是一整套任務(wù)處理機(jī)制,需要Handler、Looper梳玫、Message和MessageQueue配合爹梁。
- Looper在線程中死循環(huán),使線程不退出
- Handler發(fā)送和處理Message
- Message包含任務(wù)或者執(zhí)行任務(wù)所需的數(shù)據(jù)
- Message發(fā)送給MessageQueue提澎,MessageQueue負(fù)責(zé)Message的排序與出隊(duì)
Message是Android中的一個(gè)重要概念姚垃,它把Android應(yīng)用從Java的任務(wù)處理系統(tǒng)變成了消息處理系統(tǒng)。
Looper
要把一個(gè)線程變成Handler線程盼忌,只需在線程的run方法中調(diào)用Looper.prepare()和Looper.loop()积糯。
class LooperThread extends Thread {
public void run() {
Looper.prepare();
Looper.loop();
}
}
Looper的創(chuàng)建非常巧妙,是在Looper.prepare()中完成的谦纱。Looper實(shí)例存儲(chǔ)在ThreadLocal中看成,可以通過(guò)Looper.myLooper()獲取。Looper默認(rèn)是可以退出的跨嘉,要?jiǎng)?chuàng)建不可退出的Looper川慌,需要調(diào)用Looper.prepare(false),主線程Looper正是這樣創(chuàng)建的祠乃。要注意的是梦重,一個(gè)線程只能與一個(gè)Looper關(guān)聯(lián)。
Looper創(chuàng)建后通過(guò)Looper.loop()完成工作亮瓷。正如上文所說(shuō)琴拧,Looper.loop()在Thread.run方法中輪詢(xún)消息,使線程不退出嘱支、消息串行處理蚓胸。
// Looper.java
public static void loop() {
for (;;) {
Message msg = queue.next(); // might block
msg.target.dispatchMessage(msg);
}
}
MessageQueue
MessageQueue是在Looper的構(gòu)造函數(shù)內(nèi)創(chuàng)建的。由此可知斗塘,線程赢织、Looper和MessageQueue三者是一一對(duì)應(yīng)的亮靴。
MessageQueue的主要工作是處理Message入列和安排Message出列馍盟。MessageQueue并不是常規(guī)的先進(jìn)先出隊(duì)列,而是根據(jù)Message期望執(zhí)行的時(shí)間排列的茧吊。
- 入列:boolean enqueueMessage(Message msg, long when)
- 出列:Message next()
這兩個(gè)方法都有用到native代碼贞岭,實(shí)現(xiàn)比較復(fù)雜,就不分析了(因?yàn)闆](méi)看懂??)搓侄。
Message
Message是一個(gè)數(shù)據(jù)類(lèi)瞄桨,攜帶要執(zhí)行的任務(wù)(Runnable)或執(zhí)行任務(wù)所需的數(shù)據(jù)(what、arg1讶踪、arg2芯侥、obj、data)以及任務(wù)處理者(Handler)和任務(wù)執(zhí)行時(shí)間(when)。Message的實(shí)現(xiàn)是一個(gè)單鏈表結(jié)點(diǎn)柱查,因此可以攜帶下一條Message廓俭。MessageQueue正是基于Message單鏈表實(shí)現(xiàn)的。
由于是數(shù)據(jù)類(lèi)唉工,且應(yīng)用中需要大量Message研乒,因此Message實(shí)現(xiàn)了重用緩存池,通過(guò)Message.obtain()獲取淋硝。與MessageQueue一樣雹熬,Message緩存池也是基于其單鏈表實(shí)現(xiàn)的,不同的是緩存池是棧而非隊(duì)列谣膳。
Handler
官方文檔說(shuō)竿报,Handler與創(chuàng)建它的線程及該線程的MessageQueue關(guān)聯(lián)(When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it)。這說(shuō)的是不指定Looper的情況下继谚,Handler自動(dòng)與該線程的Looper關(guān)聯(lián)仰楚,上文說(shuō),線程犬庇、Looper和MessageQueue三者一一對(duì)應(yīng)僧界,因此默認(rèn)情況下,Handler與創(chuàng)建它的線程及該線程的MessageQueue關(guān)聯(lián)臭挽。但在創(chuàng)建Handler時(shí)是可以指定Looper的捂襟,這樣就會(huì)與Looper所在的線程和MessageQueue關(guān)聯(lián)。
不管Handler在哪個(gè)線程創(chuàng)建欢峰,它發(fā)出的消息都是在與之關(guān)聯(lián)的Looper所在的線程處理的葬荷,這正是Handler用于線程間通信的原理。由于后臺(tái)線程不允許更新UI纽帖,因此后臺(tái)線程通過(guò)一個(gè)與主線程Looper關(guān)聯(lián)的Handler(new Handler(Looper.getMainLooper()))來(lái)更新UI宠漩。
Handler發(fā)送消息有兩種方式:
- sendMessage(Message)
- post(Runnable)
但其實(shí)就一種,因?yàn)镽unnable也會(huì)轉(zhuǎn)為Message懊直。Handler發(fā)送消息還可以設(shè)置延時(shí)或定時(shí)扒吁,但最終也是一種,因?yàn)檠訒r(shí)也會(huì)被轉(zhuǎn)為定時(shí)任務(wù)室囊。因此所有的發(fā)送方式最終都調(diào)用下列方法處理:
// Handler.java
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
放入MessageQueue中的Message會(huì)被逐個(gè)取出雕崩,串行處理。有3處對(duì)象可以處理消息:
- Message自身callback(一般是post(Runnable)方式發(fā)送的消息)
- Handler創(chuàng)建時(shí)設(shè)置的mCallback
- Handler
// Handler.java
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
HandlerThread
Android應(yīng)用的大部分消息都可以由主線程處理融撞,如果需要在后臺(tái)線程中處理消息盼铁,可以創(chuàng)建一個(gè)新的Handler線程。
開(kāi)發(fā)者并不需要自己實(shí)現(xiàn)Handler線程尝偎,直接使用內(nèi)置的HandlerThread即可:
HandlerThread handlerThread = new HandlerThread("MyHandlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
源碼篇和實(shí)現(xiàn)篇
要想深入理解Handler機(jī)制饶火,光憑一篇引路文章是不夠的鹏控,更重要的是閱讀源碼、動(dòng)手實(shí)踐和總結(jié)分享肤寝。
是不是很期待源碼篇和實(shí)踐篇牧挣?你來(lái)寫(xiě)!
看好你哦~