深入理解Handler機(jī)制之引路篇

要想深入理解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)。

Handler機(jī)制工作原理

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ì)象可以處理消息:

  1. Message自身callback(一般是post(Runnable)方式發(fā)送的消息)
  2. Handler創(chuàng)建時(shí)設(shè)置的mCallback
  3. 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ě)!

看好你哦~

拓展閱讀

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市刨摩,隨后出現(xiàn)的幾起案子寺晌,更是在濱河造成了極大的恐慌,老刑警劉巖澡刹,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呻征,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡罢浇,警方通過(guò)查閱死者的電腦和手機(jī)陆赋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嚷闭,“玉大人攒岛,你說(shuō)我怎么就攤上這事“蹋” “怎么了灾锯?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)嗅榕。 經(jīng)常有香客問(wèn)我顺饮,道長(zhǎng),這世上最難降的妖魔是什么凌那? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任兼雄,我火速辦了婚禮,結(jié)果婚禮上帽蝶,老公的妹妹穿的比我還像新娘赦肋。我一直安慰自己,他們只是感情好嘲碱,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布金砍。 她就那樣靜靜地躺著,像睡著了一般麦锯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上琅绅,一...
    開(kāi)封第一講書(shū)人閱讀 51,370評(píng)論 1 302
  • 那天扶欣,我揣著相機(jī)與錄音,去河邊找鬼。 笑死料祠,一個(gè)胖子當(dāng)著我的面吹牛骆捧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播髓绽,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼敛苇,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了顺呕?” 一聲冷哼從身側(cè)響起枫攀,我...
    開(kāi)封第一講書(shū)人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎株茶,沒(méi)想到半個(gè)月后来涨,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡启盛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年蹦掐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片僵闯。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡卧抗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鳖粟,到底是詐尸還是另有隱情颗味,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布牺弹,位于F島的核電站浦马,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏张漂。R本人自食惡果不足惜晶默,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望航攒。 院中可真熱鬧磺陡,春花似錦、人聲如沸漠畜。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)憔狞。三九已至蝴悉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瘾敢,已是汗流浹背拍冠。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工尿这, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人庆杜。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓射众,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親晃财。 傳聞我的和親對(duì)象是個(gè)殘疾皇子叨橱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容