事件起因
招聘季,面試了一些Android兄弟,發(fā)現(xiàn)對基礎(chǔ)概念吃的不透.
遂成此文,打算出個一句話系列,講清楚一些android的基本概念.
讓大家在面試的時候能找到心儀的工作,并且不被面試官鄙視.....
此篇內(nèi)容主要將的Android消息機制相關(guān)的內(nèi)容.
正式開始
Handler,Looper機制是android中的消息系統(tǒng),兩個線程間傳遞消息,(進程間也可以使用Messenger傳遞Message).
主要涉及的類
-
Looper
負(fù)責(zé)從MessageQueue中獲取消息及將消息分發(fā)到對應(yīng)的Handler. -
Thread
任務(wù)執(zhí)行的線程環(huán)境. -
ThreadLocal
負(fù)責(zé)保存Thread關(guān)聯(lián)的Looper. -
ThreadLocalMap
ThreadLocal中具體保存數(shù)據(jù)的類.內(nèi)部使用Entry數(shù)組.來保存數(shù)據(jù). -
MessageQueue
保存Message的容器,內(nèi)部實現(xiàn)為鏈表. -
Handler
向MessageQueue中插入消息,以及對收到的Looper分發(fā)的消息的處理. -
Message
消息載體.主要如下屬性-
callback
任務(wù)的runnable對象 -
when
message需要被dispatch的執(zhí)行時間 -
obj
message中傳遞的對象.
-
進行線程間通訊的主要流程如下
- 創(chuàng)建
Thread - 在
Thread.run方法中依次調(diào)用Looper.prepare,Looper.loop.-
Looper.prepare
主要用來將Looper關(guān)聯(lián)到當(dāng)前thread的threadLocal.
執(zhí)行Looper.prepare時,會判斷當(dāng)前線程如果沒有關(guān)聯(lián)looper時,
就會調(diào)用threadLocal.set()方法向threadLocal中添加一個創(chuàng)建的Looper對象. -
Looper.loop
主要用來循環(huán)從Looper的MessageQueue中取對象.
Looper.loop方法內(nèi)部為一個無限循環(huán)的for循環(huán).
在for循環(huán)中通過調(diào)用messqueue.next()方法來獲取到隊列中的message.
-
- 創(chuàng)建
Handler關(guān)聯(lián)到該Looper - 通過創(chuàng)建的
Handler.sendMessage方法將message傳遞到創(chuàng)建該Handler所在Looper中的MessageQueue. -
MessageQueue在判斷其中有message.when小于當(dāng)前時間的message時,將該消息返回給Looper.loop -
Looper.loop在拿到message后,調(diào)用message.target所關(guān)聯(lián)的Handler對象的dispatch方法,
從而實現(xiàn)了在創(chuàng)建Handler所在線程中執(zhí)行消息的能力.
如果創(chuàng)建Handler跟通過該Handler發(fā)送Message不在同一個線程,則也就實現(xiàn)了線程間通訊的能力.
流程中涉及到的一些技術(shù)點
MessageQueue保存在哪
MessageQueue保存在Looper中,Looper保存在ThreadLocal中,
ThreadLocal保存在當(dāng)前Thread中的ThreadLocal.ThreadLocalMap中.
MessageQueue中的保存Message的數(shù)據(jù)結(jié)構(gòu)
MessageQueue對象中保存著Message對象,MessageQueue中保存的Message對象是以鏈表數(shù)據(jù)結(jié)構(gòu)保存的.
通過Message.next指向下一個Message對象.
Message對象的緩存機制
對象的頻繁創(chuàng)建銷毀,會消耗cpu的性能,而消息傳遞又會頻繁創(chuàng)建消息對象,所以message對象是會被緩存起來的.
Message對象通過Message.obtain()可以獲得,.obtain()方法可以看作是一個工廠方法,
Message內(nèi)部維護著一個緩沖區(qū),緩沖區(qū)的數(shù)據(jù)結(jié)構(gòu)也可看做時鏈表形式.
通過Message.next方法指向下一個可被復(fù)用的Message.
緩存區(qū)的大小為50,所以最多可緩存50個Message對象.
ThreadLocal是什么,如何實現(xiàn)的
ThreadLocal是一種保存變量的線程安全的類.
在多線程環(huán)境下能安全使用變量,最好的方式是在每個線程中定義一個local變量.
這樣對線程中的local變量做修改就只會影響當(dāng)前線程中的該變量,因此變量也就是線程安全的.
這種保證變量線程安全的思想,實際上就是ThreadLocal的實現(xiàn).
Threadlocal在調(diào)用threadLocal.get方法時,會獲取當(dāng)前thread的threadLocalMap.
threadLocalMap是thread中的一個屬性.
第一次調(diào)用ThreadLocal.get()方法時,會先判斷當(dāng)前線程對應(yīng)的threadlocalMap是否被創(chuàng)建了,
如果沒創(chuàng)建則會創(chuàng)建ThreadLocalMap,并把該對象賦值給thread.sThreadLocal對象.后續(xù)再獲取當(dāng)前thread的threadLocalMap時,就會取該賦值對象.
ThreadLocalMap就是用來保存線程中需要保存的變量的對象了.
因為threadLocalMap是賦值給當(dāng)前thread的,屬于thread的內(nèi)部變量,
所以每個線程的threadlocalMap就都是不同的對象,也就是上面說的threadlocal是線程安全的原因了.
ThreadLocalMap內(nèi)部實際上是一個Entry[],用來保存Entry對象的數(shù)組.
Entry對象是繼承weakReference的,其中Entry的key為ThreadLocal對象,value為threadLocal需要保存的變量值.
調(diào)用ThreadLocal.set方法時,會向threadLocalMap中添加一個Entry對象.
調(diào)用get方法時,是通過將調(diào)用的threadLocal對象本身作為key,來遍歷threadLocalMap數(shù)組.
當(dāng)threadLocal等于Entry[]中的key時,則返回該Entry中的value.
Looper中的ThreadLocal對象為啥是static的?
其實threadLocal對象是否定義為static對ThreadLocal類本身的作用來講是沒影響的.
但是因為Looper.prepare方法是定義為static的,而prepare中又需要對threadlocal進行訪問,所以Looper中的threadLocal對象也就必須定義為static的了.
ThreadLocal中為啥只能保存一個Looper對象
threadLocalMap可以保存無線多的數(shù)據(jù),但是每個線程只能關(guān)聯(lián)一個Looper對象.
因為threadLocal中會保存當(dāng)前線程關(guān)聯(lián)的Looper對象,這個限制是Looper做的.
Looper是如何從MessageQueue中獲取將要分發(fā)的message的
通過Looper.loop方法來獲取并分發(fā)message.
Looper.loop內(nèi)部為一個無限循環(huán)的for循環(huán).在for循環(huán)中通過調(diào)用messqueue.next()方法來獲取到隊列中的message.
messagequeue.next()方法內(nèi)部會調(diào)用nativePollOnce方法,該方法會阻塞線程.
該方法的作用簡單說,就是當(dāng)消息隊列中沒消息時,阻塞掉當(dāng)前執(zhí)行的線程.避免過度的cpu消耗.
關(guān)于nativePollOnce阻塞線程的解釋
當(dāng)消息隊列中有消息時,messagequeue.next()方法內(nèi)部會判斷現(xiàn)有的消息鏈表中的消息的message.when屬性是否小于當(dāng)前時間.
如果小于當(dāng)前時間,則next方法會將該條消息返回給loop中調(diào)用next函數(shù)的地方,這樣looper就拿到了將要dispatch的消息了.
所以是通過Messagequeue來判斷message是否能被looper進行分發(fā)的.
Looper是如何分發(fā)message的?
當(dāng)looper.loop方法中獲取到message時,會調(diào)用message.target.dispatch方法.
其中message.target屬性是在Handler內(nèi)部調(diào)用Handler.enqueueMessage方法時,將當(dāng)前調(diào)用方法的handler對象設(shè)置到Message中的.
通過message.target.dispatchMessage方法,將該message的執(zhí)行環(huán)境切換到了該handler對應(yīng)的thread中.
handler可以實現(xiàn)handleMessage方法來處理消息.
調(diào)用完handler.dispatchMessage方法后,則會將該消息通過recycleUnchecked方法,對message進行回收.
在Message.recycleUnchecked方法中會重置該message對象(將message對象相關(guān)屬性置空),并將該對象添加到Message緩存區(qū)中.
加入緩存去的過程也就是將該對象加入到緩存區(qū)鏈表中.加入緩沖區(qū)的message后面可通過Message.obtain方法來獲取.
結(jié)尾
這文章寫了一天吧,希望能幫助到需要面試Android的同學(xué).
喜歡的麻煩點個贊,關(guān)注下.