消息機制概述
Android 系統(tǒng)在設計的初期就已經(jīng)考慮到了 UI 的更新問題呐能,由于 Android 中的 View 是線程不安全的泉褐,然而程序中異步處理任務結束后更新 UI 元素也是必須的租冠。這就造成了一個矛盾掸犬,最簡單的解決方法肯定是給 View 加同步鎖使其變成線程安全的跟匆。這樣做不是不可以甜害,只是會有兩點壞處:
1 加鎖會有導致效率底下
2 由于可以在多個地方更新 UI,開發(fā)就必須很小心操作卑硫,開發(fā)起來就很麻煩徒恋,一不小心就出錯了。
基于以上兩個缺點欢伏,這種方式被拋棄入挣。于是,設置一個線程專門處理 UI控件的更新硝拧,如果其他線程也需要對 UI 進行更新径筏,不好意思,您把您想做的告訴那個專門處理 UI 線程的家伙河爹,讓它幫你做匠璧。大家各有各的任務,井水不犯河水咸这,各司其職夷恍,效率才會高,
那么您也看出來了媳维,消息機制其實可以很簡單的用一句話概括酿雪,就是:其他線程通過給特定線程發(fā)送消息,將某項專職的工作侄刽,交給這個特定的線程去做指黎。比如說其他線程都把處理 UI 顯示的工作通過發(fā)送消息交給 UI 線程去做。?
消息機制相關類
消息主要設計到下面幾個類:
Handler:這是消息的發(fā)出的地方州丹,也是消息處理的地方醋安。
Looper:這是檢測消息的地方。
MessageQueue: 這是存放消息的地方墓毒,Handler 把消息發(fā)到了這里吓揪,Looper 從這里取出消息交給 Handler 進行處理
Message:嗚嗚嗚…他們發(fā)的是我,處理的也是我所计。
Thread:我在這里專門指代的是柠辞,處理消息的線程。消息的發(fā)送是在別的線程主胧。
源碼跟蹤--梳理消息發(fā)送接收過程
咱們先來走一個在子線程想要更新UI的流程:
1?在主線程新建一個 Handler 對象叭首,在構造方法中傳入一個實現(xiàn) Handler.Callback 接口的匿名類的對象习勤,實現(xiàn)接口中的 handleMessage 方法
2?在非 UI 線程使用 Handler 的 sendMessage 或者 post 方法發(fā)送一個消息
3?然后 handleMessage 方法會在不久的將來馬上執(zhí)行,實現(xiàn)更新 UI 的操作焙格。
關于類Handler:
Handler 擁有 Looper 的引用图毕,通過得到 Looper 對象獲得 Looper 中保存的 MessageQueue 對象
Handler 擁有 MessageQueue 的引用,使 Handler 得以擁有發(fā)送消息(將 Message 放入 MessageQueue )的能力
Handler 擁有 Handler.Callback 的引用眷唉,使得 Handler 可以方便的進行消息的處理吴旋。
根據(jù)上面的1 2 3 步,我們跟進源碼看看:
1 創(chuàng)建handler
handler構造方法源碼
在handler中獲取了mLooper厢破,mQueue對象,主要看mLooper是怎么獲取的治拿,Looper.myLooper();再跟進這個方法:
Looper類
可以看到是由ThreadLocal維護的摩泪,我們知道ThreadLocal可以保存對應線程的變量,那么必定保存了當前線程的Looper劫谅。我們查找ThreadLocal.set()方法:
Looper類方法
可以看到是在調用Looper.prepare()時設置的见坑,在主線程中是誰調用Looper.prepare()的呢?沒錯捏检,就是ActivityThread(主線程)荞驴,在創(chuàng)建主線程的main方法里調用:
ActivityThread中的main方法
那么就可以確定,上面ThreadLocal存儲的Looper就是主線程的Looper贯城。mQueue自然也是主線程的Looper的變量熊楼。然后就看發(fā)消息了,看看 Handler.sendMessage 干了啥,
handler類發(fā)送消息
可以看到能犯,Handler類中發(fā)送消息最后調用了MessageQueue的enqueueMessage()方法鲫骗,并且把msg.target設置成了handler本身,這在之后分析中用到踩晶,繼續(xù)跟進:
MessageQueue類方法
到這里执泰,我們發(fā)現(xiàn)handler已經(jīng)成功法一個消息放到了主線程的MessageQueue中了,接下來就是取消息操作了渡蜻。只要主線程中調用了Looper.loop()方法术吝,那就啟動了一個死循環(huán),會從MessageQueue中不停的取消息(無消息就阻塞)茸苇。
Looper的loop方法
可以看到排苍,loop()方法開啟了一個死循環(huán),不斷從MessageQueue取消息税弃,并調用msg.target(Handler)去調用dispatchMessage(msg)方法纪岁。那就是調用了handler的dispatchMessage(msg)方法
handler類方法
這里分三種情況,
? ? * 如果Message中callback對象不為空(這是調用handler.post(Runnable)方法發(fā)送的消息)则果,就調用callback的run方法
? ? * 否則如果創(chuàng)建Handler的時候如果設置了Callback就調用創(chuàng)建時候的傳入的實現(xiàn)Handler.Callback接口的類的對象的handleMessage方法幔翰,看這就是回調方法被調用的地方漩氨。
? ? * 再如果沒有mCallback對象,就調用自身的handleMessage方法遗增,為了Handler的子類復寫了該方法的時候叫惊,方便調用,如做修,IntentService里的ServiceHandler就是繼承自Handler的霍狰,并且重寫了handleMessage方法。
我們創(chuàng)建handler時實現(xiàn)了Handler.Callback接口饰及,所以就跳到一開始創(chuàng)建handler的地方處理消息啦蔗坯。
小結
ok,再整理一下思路:1 在主線程創(chuàng)建handler實現(xiàn)Handler.Callback接口燎含,主線程一運行就存在Loop和MessageQueue宾濒。2 其他線程通過主線程的該handler發(fā)送msg,發(fā)送到了主線程的MessageQueue屏箍。3 主線程的Looper從MessageQueue循環(huán)取消息绘梦,取到該消息就調用msg.target(handler).dispatchMessage(msg),也就跟著調用了handler的handleMessage()。
另外赴魁,如果非主線程要創(chuàng)建handler對象卸奉,必須要調用Looper.prepare()創(chuàng)建一個Looper和Looper.loop()開啟消息循環(huán)。