對(duì)于我們來(lái)說(shuō)Handler機(jī)制是日常開(kāi)發(fā)中經(jīng)常用到的碳蛋,我們可以使用它輕松實(shí)現(xiàn)線(xiàn)程間的切換和延時(shí)操作龄砰。今天我們就來(lái)分析一下Handler的實(shí)現(xiàn)原理塑径。我們先來(lái)看一下Handler機(jī)制的中重要的四個(gè)類(lèi)和一個(gè)工具葡盗。
Handler 用于消息的發(fā)送和接受消息
Message 消息
MessageQueue 單向綁定的消息列表
Looper 消費(fèi)消息
ThreadLocal 數(shù)據(jù)儲(chǔ)存
一审磁、ThreadLocal
ThreadLocal主要作用是將數(shù)據(jù)綁定當(dāng)線(xiàn)程上酒来,接下來(lái)我們主要是看它的get和set方法卢未。
上面的get方法中會(huì)先獲取當(dāng)前線(xiàn)程,然后以該線(xiàn)程為key去查找對(duì)應(yīng)的Value堰汉,如果Value為null時(shí)會(huì)返回setInitialValue()方法中的值辽社,其實(shí)setInitialValue()方法放回的就是一個(gè)初始值默認(rèn)為null我們可以通過(guò)重寫(xiě)initialValue()方法來(lái)設(shè)置初始值。
set方法就簡(jiǎn)單多了翘鸭,它就是以當(dāng)前的線(xiàn)程為key進(jìn)行數(shù)據(jù)的存儲(chǔ)滴铅。這樣就能把數(shù)據(jù)綁定到線(xiàn)程上了。
二矮固、Message
消息對(duì)象就是一個(gè)數(shù)據(jù)類(lèi)失息。Message的創(chuàng)建方法有兩種,一個(gè)是直接使用new關(guān)鍵字另一個(gè)就是通過(guò)obtain方法進(jìn)行創(chuàng)建了档址。兩個(gè)種方法有什么區(qū)別呢為什么推薦我們使用obtain方法創(chuàng)建Message盹兢。我們分析obtain方法的時(shí)要對(duì)應(yīng)的看一下它的回收方法recycleUnchecked
如果我們直接去看obtain方法的話(huà)可能理解起來(lái)有一定的困難。我們可以先從它的回收方法開(kāi)始分析守伸。我們主要看一下回收方法中同步代碼塊的代碼绎秒,它先判斷sPoolSize是否小于MAX_POOL_SIZE,sPoolSize的初始值為0 MAX的值為50 接下來(lái)就是next = sPool尼摹;next和sPool的類(lèi)型都是Message见芹,sPool是個(gè)靜態(tài)的屬性。他將要回收的message的next賦值為sPool再將Message賦值給靜態(tài)屬性sPool然后sPoolSize++蠢涝,他這是干呢玄呛?其實(shí)sPool是Message內(nèi)部的一個(gè)大小為50的單向緩存列表。執(zhí)行回收方法時(shí)先判斷緩存列表的個(gè)數(shù)和二,如果小于50就將回收的Message添加到列表中徘铝。
知道sPool是一個(gè)緩存列表之后我們?cè)偃シ治鰋btain方法就簡(jiǎn)單了,它就是先去sPool中去取緩存惯吕,沒(méi)有緩存時(shí)就通過(guò)new 關(guān)鍵字來(lái)創(chuàng)建一個(gè)新的返回惕它。
Message中的target屬性我們也要知道,他是用于存儲(chǔ)發(fā)送該消息的handler废登。在handler發(fā)送消息時(shí)進(jìn)行的賦值淹魄。
三、Handler
Handler的作用就是進(jìn)行消息的發(fā)送與接送堡距。
我們先看一下Handler創(chuàng)建過(guò)程中都做了什么甲锡。
標(biāo)注1處就是通過(guò)ThreadLocal獲取當(dāng)前線(xiàn)程綁定的Looper兆蕉。這里有一個(gè)常見(jiàn)的問(wèn)題就是在子線(xiàn)程中為什么不是直接使用Handler,就是因?yàn)樵谧泳€(xiàn)程中創(chuàng)建Handler時(shí)不能獲取子線(xiàn)程對(duì)應(yīng)的Looper從而拋出了異常搔体,那為什么子線(xiàn)程不能直接創(chuàng)建Handler而主線(xiàn)程卻可以呢恨樟?這個(gè)我們?cè)贚ooper那里進(jìn)行分析。標(biāo)注2處就是獲取Looper中的MessageQueue賦值給成員變量mQueue疚俱。接下來(lái)看一下發(fā)送消息的過(guò)程劝术。
handler發(fā)送消息的方法有很多個(gè),但都是在計(jì)算出一個(gè)時(shí)間戳之后去調(diào)用了enqueueMessage方法呆奕。方法中為msg.target賦值為當(dāng)前的handler养晋。最后調(diào)用MessageQueue的enqueueMessage方法將消息添加到消息列表中。以上就是handler發(fā)送消息的過(guò)程梁钾。對(duì)于handler接受消息的過(guò)程我們?cè)贚ooper中進(jìn)行分析绳泉,大致過(guò)程就是Looper拿到消息后調(diào)用通過(guò)message的target獲取對(duì)應(yīng)的handler再調(diào)用handler的dispatchMessage方法回調(diào)我們重寫(xiě)的方法。
四姆泻、Looper
Looper的作用就是循環(huán)的取出MessageQueue中Message并將他們分發(fā)給對(duì)應(yīng)的Handler零酪。
在講Handler的時(shí)候我們說(shuō)到?jīng)]什么主線(xiàn)程中可以取到Looper而子線(xiàn)程中就取不到呢。這是因?yàn)橄到y(tǒng)在ActivityThread的main方法中進(jìn)行了創(chuàng)建拇勃,我們看一下main方法四苇。
在main方法中主要是調(diào)用了Looper的prepareMainLooper方法和loop方法。我們先看一下prepareMainLooper方法
prepareMainLooper方法就是創(chuàng)建了Looper并將該Looper保存到了ThreadLocal中方咆,同時(shí)創(chuàng)建了對(duì)應(yīng)了的MessageQueue月腋。這樣主線(xiàn)程上面就綁定了一個(gè)Looper。那我們我們要想在子線(xiàn)程中使用handler的話(huà)要手動(dòng)調(diào)用Looper.prepare()在子線(xiàn)程中創(chuàng)建一個(gè)Looper瓣赂,然后調(diào)用loop方法開(kāi)啟循環(huán)榆骚。
loop方法比較長(zhǎng)我們主要看四個(gè)地方就可以了,標(biāo)注1 在調(diào)用loop方法后會(huì)開(kāi)啟一個(gè)死循環(huán)煌集。標(biāo)注 2 此處調(diào)用了MessageQueue的next方法來(lái)獲取列表中的消息妓肢,沒(méi)有事消息時(shí)阻塞。標(biāo)注3 獲取到message后通過(guò)它的target屬性獲取對(duì)應(yīng)的handler并調(diào)用dispatchMessage方法苫纤。標(biāo)注4 message處理完成后調(diào)用它的回收方法职恳。
五、MessageQueue
用于存儲(chǔ)消息方面。我們主要看一下它的儲(chǔ)存方法和取出方法。先看他的儲(chǔ)存方法色徘。
它主要是通過(guò)when這個(gè)是延時(shí)時(shí)間來(lái)判斷message添加到mMessage中的位置恭金。mMessage也是一個(gè)單項(xiàng)列表。
next方法比較長(zhǎng)它的主要功能就是循環(huán)的返回列表中的message褂策,沒(méi)有值的時(shí)候通過(guò)native方法就行阻塞横腿。
六颓屑、總結(jié)
我們對(duì)Handler機(jī)制中的各個(gè)部分進(jìn)行了逐一的分析,已經(jīng)大概知道各部分的作用及整個(gè)機(jī)制的運(yùn)作過(guò)程耿焊【镜耄可以用下圖簡(jiǎn)單表示。
該文檔是自己的學(xué)習(xí)記錄如有錯(cuò)誤歡迎指出罗侯。