概述
很多android初學(xué)者對android 中的handler不是很明白斜棚,其實Google參考了Windows的消息處理機(jī)制,
在Android系統(tǒng)中實現(xiàn)了一套類似的消息處理機(jī)制边败。在下面介紹handler機(jī)制前脑蠕,首先得了解以下幾個概念:
####1. Message
消息皿曲,理解為線程間通訊的數(shù)據(jù)單元摩疑。例如后臺線程在處理數(shù)據(jù)完畢后需要更新UI,則可發(fā)送一條包含更新信息的Message給UI線程鞋怀。
####2. Message Queue
消息隊列双泪,用來存放通過Handler發(fā)布的消息,按照先進(jìn)先出執(zhí)行密似。
####3. Handler
Handler是Message的主要處理者焙矛,負(fù)責(zé)將Message添加到消息隊列以及對消息隊列中的Message進(jìn)行處理。
####4. Looper
循環(huán)器残腌,扮演Message Queue和Handler之間橋梁的角色村斟,循環(huán)取出Message Queue里面的Message贫导,并交付給相應(yīng)的Handler進(jìn)行處理。
####5. 線程
UI thread 通常就是main thread蟆盹,而Android啟動程序時會替它建立一個Message Queue孩灯。
每一個線程里可含有一個Looper對象以及一個MessageQueue數(shù)據(jù)結(jié)構(gòu)。在你的應(yīng)用程序里逾滥,可以定義Handler的子類別來接收Looper所送出的消息峰档。
好了,下面是正文~
Handler作用:
因為在Android中寨昙,主線程不建議做耗時的操作讥巡,子線程不建議跟新UI,但是Android開發(fā)舔哪,其實就是搭建好頁面欢顷,將服務(wù)器的數(shù)據(jù)展示到頁面上,所以我網(wǎng)絡(luò)請求使用會非常頻繁捉蚤,而網(wǎng)絡(luò)請求屬于耗時操作吱涉,需要放到子線程完成,但一般情況下也不會通過子線程更新UI外里,需要將請求成功的數(shù)據(jù)發(fā)送到主線程進(jìn)行UI更新,所以一般會使用到handler特石。
Handler執(zhí)行流程:
首先handler作為任務(wù)執(zhí)行者盅蝗,一般創(chuàng)建在主線程,當(dāng)子線程有需要發(fā)送的數(shù)據(jù)姆蘸,通過創(chuàng)建message對象墩莫,使用handler對象將消息發(fā)送到messagequeue,messagequeue遵循了隊列先進(jìn)先出的原則逞敷,當(dāng)主線程的looper循環(huán)消息的時候狂秦,會按照messagequeue隊列的順序循環(huán)消息,并將消息給到任務(wù)執(zhí)行者h(yuǎn)andler去執(zhí)行任務(wù)推捐。
Handler執(zhí)行原理:
Handler創(chuàng)建完成后裂问,內(nèi)部的Looper以及MessageQueue就可以和Handler一起協(xié)同工作,然后通過Hadler的post方法將一個Runnable投遞到Handler內(nèi)部的Looper中去處理牛柒,也可以通過Handler的send方法發(fā)送一個消息堪簿,這個消息會在Looper中做處理。Post最終也是通過send來完成的皮壁。當(dāng)Handler的send方法被調(diào)用時椭更,他會調(diào)用MessageQueue的enqueueMessage方法將這個消息放入消息隊列中,然后Looper發(fā)現(xiàn)有新消息到來時蛾魄,就會處理這個消息虑瀑,最終消息中的Runnable或者Handler的handlerMessage方法就會被調(diào)用湿滓。Looper試運(yùn)行在Handler所在的線程,所以就把業(yè)務(wù)邏輯切換到主線程了
1.ThreadLocal的工作原理
(1)定義:ThreadLocal是線程內(nèi)部的數(shù)據(jù)存儲類舌狗,通過他可以在指定的線程中存儲數(shù)據(jù)叽奥,該數(shù)據(jù)只有在指定線程中可以獲取
(2)使用場景:當(dāng)某些數(shù)據(jù)是以線程為作用域并且不同線程具有不同的數(shù)據(jù)副本的時候另外還可以使用在復(fù)雜邏輯下的對象傳遞,比如監(jiān)聽器的傳遞
ThreadLoal的值在table數(shù)組中的存儲位置總是為ThreadLocal的reference字段所標(biāo)識的對象的下一個位置把夸。ThreasdLoacal的set和get方法所操作的對象都是當(dāng)前線程的localValues對象的table數(shù)組而线,因此在不同的線程中訪問同一個ThreadLocal的set和get方法,他們對ThreadLocal所做的讀寫操作僅限于各自線程的內(nèi)部恋日,從而實現(xiàn)在多個線程中互不干擾的存儲和修改數(shù)據(jù)膀篮。
Looper的工作原理
Looper在Android的消息機(jī)制中扮演著消息循環(huán)的角色,就是不停的從MessageQueue中查看是否有新消息岂膳,如果有消息就立刻處理誓竿,否則就一直阻塞在哪里首先在構(gòu)造方法中創(chuàng)建一個MessageQueue即隊列消息,然后將當(dāng)前的對象保存起來Looper除了prepare方法外谈截,還提供了prepareMainLooper方法筷屡,這個方法主要是給主線程也就是ActivityThread創(chuàng)建Looper使用的,其本質(zhì)也是通過prepare方法來實現(xiàn)的簸喂。由于主線程的Looper比較特殊毙死,所以Looper提供了一個getMainLooper方法,通過它可以再任何地方獲取到主線程的Looper喻鳄。Looper也是可以退出的扼倘,Looper提供了quit和quitSafely來退出一個Looper,二者的區(qū)別是:quit會直接退出Looper除呵,而quieSafely只是假定一個特殊標(biāo)記再菊,然后把消息隊列中的已有消息處理完畢后才安全退出。Looper退出后颜曾,通過Handler發(fā)送消息失敗纠拔,這個時候Handler的send方法會返回false。在子線程中泛豪,如果手動為其創(chuàng)建Looper稠诲,那么在所有的事情完成以后應(yīng)該調(diào)用quit方法來終止消息循環(huán),否則這個子線程就會一直處于等待狀態(tài)诡曙,而如果想退出Looper以后吕粹,這個線程就會立刻終止,岗仑,因此建議不需要的時候終止Looperloop方法是個死循環(huán)匹耕,唯一跳出循環(huán)的方式是MessageQueue的next方法返回null。當(dāng)Looper的quit方法被調(diào)用時荠雕,Looper就會調(diào)用MessageQueue的quit或者quitSafely方法來通知消息隊列退出稳其,當(dāng)消息隊列被標(biāo)記為退出狀態(tài)時驶赏,它的next方法就返回null。Loop必須退出既鞠,否則loop循環(huán)就會無限循環(huán)下去煤傍。loop方法會調(diào)用MessageQueue的next方法,而next方法是一個當(dāng)沒有消息時 是一個阻塞線程嘱蛋,便會導(dǎo)致Looper也會阻塞在那里蚯姆。當(dāng)MessageQueue的next方法返回了新的消息,Looper就會處理這條消息:msg.target.disapatchMessage洒敏,這里msg.target是發(fā)送這條消息的Handler對象龄恋,消息交給dispatchMessage方法來處理。而這個dispatchMessage方法是在創(chuàng)建Handler時所用的Looper中執(zhí)行凶伙。這樣就把代碼邏輯切換到主線程郭毕。
handler,looper和Measge之間的如何協(xié)作的
主線程向子線程發(fā)送消息:
首先handler肯定是要創(chuàng)建到子線程當(dāng)中函荣,用于接收主線程發(fā)來消息進(jìn)行處理显押,但是,因為子線程沒有l(wèi)ooper對象傻挂,首先需要調(diào)用looper.prepare(),當(dāng)主線程發(fā)來消息后乘碑,已經(jīng)準(zhǔn)備好的looper同樣會去消息隊列當(dāng)中循環(huán)消息,交給handler金拒,但handler真正能夠使用該數(shù)據(jù)還得調(diào)用looper.loop();
Measge可以通過wait來區(qū)分消息
Mesage通過takte來區(qū)分handler