消息機(jī)制的概述
- Handler:Android消息機(jī)制的上層接口画侣,日常開發(fā)中被開發(fā)者用來更新UI缰趋,可以輕松將任務(wù)切換到Handler所在線程去執(zhí)行揉燃。
- Android的消息機(jī)制:主要是指Handler的運(yùn)行機(jī)制迎卤,需要底層的MessageQueue和Looper支撐墩弯。MessageQueue是消息隊(duì)列它匕,結(jié)構(gòu)是單向鏈表展融,而Looper是以死循環(huán)的形式查詢消息并處理新消息。
- ThreadLocal:Looper中的一個成員變量豫柬,在不同線程中互不干擾存儲并提供數(shù)據(jù)告希,通過ThreadLocal可以輕松獲取每個線程中的Looper。線程默認(rèn)沒有Looper烧给,如果要用Handler燕偶,就必須為線程創(chuàng)建Looper。
- UI線程:本質(zhì)是ActivityThread础嫡,它被創(chuàng)建的時候就會初始化Looper指么,所以在主線程中默認(rèn)可以使用Handler。
- 只能在主線程中訪問UI榴鼎,是通過ViewRootImpl的checkThread方法檢查的伯诬,之所以有這個限制,是因?yàn)锳ndroid的UI控件是非線程安全的巫财,如果多個線程同事訪問可能導(dǎo)致UI控件處于不可預(yù)期狀態(tài)盗似。
Android消息機(jī)制分析
- ThreadLocal工作原理
- ThreadLocal是一個線程內(nèi)部數(shù)據(jù)存儲內(nèi),在指定線程存儲數(shù)據(jù)平项,數(shù)據(jù)存儲之后赫舒,只能在指定線程中獲取到數(shù)據(jù)悍及,其他線程無法獲取數(shù)據(jù)。
- 使用場景:①數(shù)據(jù)以線程為作用域接癌,且不同線程有不同數(shù)據(jù)副本心赶,就可以考慮使用ThreadLocal ②復(fù)雜邏輯下的對象傳遞
- get/set方法理解:首先獲取當(dāng)前線程t,然后獲取t.threadLocals:ThreadLocalMap對象扔涧,set方法會把ThreadLocal作為key园担,泛型T作為value存儲。如果threadLocals對象為空枯夜,那么先創(chuàng)建這個對象弯汰,然后存儲key/value。get也是同樣的邏輯湖雹。
- MessageQueue工作原理
- 主要包含兩個操作咏闪,插入和讀取,讀取伴隨刪除摔吏。插入和讀取分別對應(yīng)enqueueMessage和next鸽嫂,enqueueMessage作用是往隊(duì)列中插入一條消息,next是從隊(duì)列中讀取一條消息并將其從消息隊(duì)列中移除征讲。next是一個無限循環(huán)方法据某,隊(duì)列中沒有消息,一直阻塞在這里诗箍,有新消息時癣籽,next方法返回消息并從單鏈表中移除該消息。
- Looper工作原理
- 在線程中通過prepare方法初始化Looper對象滤祖,prepareMainLooper是專門用來給主線程ActivityThread創(chuàng)建Looper對象的筷狼,本質(zhì)也是調(diào)用prepare方法。在任何線程中都可以通過getMainLooper獲取主線程的Looper匠童。
- Looper的構(gòu)造方法中會初始化一個MessageQueue對象埂材,并且保存當(dāng)前線程。
- 調(diào)用loop方法開啟消息循環(huán)汤求,loop方法是一個死循環(huán)俏险,會一直從MessageQueue中通過next方法獲取消息,并且處理消息扬绪。當(dāng)沒有消息時寡喝,next方法阻塞,導(dǎo)致loop方法也一直阻塞勒奇。唯一跳出循環(huán)的方式是MessageQueue的next方法中返回null预鬓。
- 可以通過Looper的quit和quitSafety退出一個Looper。quit直接退出Looper,而quitSafety是設(shè)置一個退出標(biāo)記格二,把消息隊(duì)列中的消息處理完畢之后安全退出劈彪。Looper退出后,Handler發(fā)送消息會失敗顶猜,send方法返回false沧奴。Looper的quit或quitSafety也會調(diào)用MessageQueue中的對應(yīng)方法,把隊(duì)列標(biāo)記為退出狀態(tài)长窄,
這時候next方法就會返回null
滔吠。子線程中的Looper使用完后都需要調(diào)用quit或quitSafety方法,不然子線程一直處于等待狀態(tài)挠日,無法結(jié)束疮绷。
- Handler的工作原理
- Handler包含消息的發(fā)送和接收過程,消息發(fā)送通過一些列post方法和send方法實(shí)現(xiàn)嚣潜,post方法的本質(zhì)也是調(diào)用send方法冬骚。而發(fā)送消息的過程很簡單,就是往Looper.MessageQueue中插入一條消息懂算。
- Handler發(fā)送消息后只冻,MessageQueue通過next把消息交給Looper處理,Looper調(diào)用Handler的dispatchMessage方法计技。
- dispatchMessage中有三層邏輯:第一:如果消息Message設(shè)置了callback(Runnable對象)喜德,那么直接執(zhí)行這個Runnable。第二:如果Handler設(shè)置了Handler.Callback垮媒,則執(zhí)行callback的handlerMessage方法住诸。第三:如果第一第二不滿足,則執(zhí)行Handler自身的handleMessage方法
- 從上面的分析可以看出涣澡,Handler的實(shí)現(xiàn)有兩種方式:
//派生Handler子類創(chuàng)建Handler對象
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO: 2018/7/9 處理消息
}
};
或
//通過傳參Handler.Callback創(chuàng)建Handler對象
Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// TODO: 2018/7/9 處理消息,false=需要進(jìn)一步處理丧诺,true=不需要進(jìn)一步處理
return false;
}
});
- 主線程的消息循環(huán)
- 主線程ActivityThread的入口方法為main,在main方法中調(diào)用Looper.prepareMainLooper創(chuàng)建主線程的Looper和MessageQueue(Looper構(gòu)造方法中初始化)。然后調(diào)用looper.loop開始主線程消息循環(huán)租副。
- 開啟消息循環(huán)之后郑藏,主線程還需要一個Handler和消息隊(duì)列交互,這個Handler就是ActivityThraed.H呵晚,它內(nèi)部定義了一組消息蜘腌,包括四大組件的生命周期消息,如下了所示:
private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
public static final int PAUSE_ACTIVITY_FINISHING= 102;
public static final int STOP_ACTIVITY_SHOW = 103;
public static final int STOP_ACTIVITY_HIDE = 104;
public static final int SHOW_WINDOW = 105;
public static final int HIDE_WINDOW = 106;
public static final int RESUME_ACTIVITY = 107;
public static final int SEND_RESULT = 108;
public static final int DESTROY_ACTIVITY = 109;
public static final int BIND_APPLICATION = 110;
public static final int EXIT_APPLICATION = 111;
public static final int NEW_INTENT = 112;
public static final int RECEIVER = 113;
public static final int CREATE_SERVICE = 114;
public static final int SERVICE_ARGS = 115;
public static final int STOP_SERVICE = 116;
...
}
一個問題
關(guān)于Handler消息機(jī)制饵隙,經(jīng)常會有這樣一個問題:Looper.loop死循環(huán)為什么不會卡死?
要解答這個問題撮珠,首先,我們來說一下線程和進(jìn)程金矛,每個應(yīng)用運(yùn)行時都會創(chuàng)建一個進(jìn)程芯急,多數(shù)情況下App運(yùn)行在一個進(jìn)程中勺届,除非指定android:process
或者通過native代碼fork進(jìn)程。在CPU看來娶耍,線程是一段可執(zhí)行代碼免姿,代碼執(zhí)行完畢,線程生命周期結(jié)束榕酒,退出線程胚膊。
而對于主線程,肯定不希望運(yùn)行一段時間就自動退出想鹰,那么如何保證一直存活紊婉,簡單的做法是通過死循環(huán)讓代碼一直執(zhí)行下去。
那么死循環(huán)是否會浪費(fèi)CPU資源呢杖挣?其實(shí)不會肩榕,主線程的MessageQueue沒有消息是,變阻塞在loop的next中惩妇,此時線程釋放CPU資源進(jìn)入休眠狀態(tài)株汉,直到下個消息發(fā)生,喚醒主線程工作歌殃。所以主線程多數(shù)情況處于休眠狀態(tài)乔妈,并不會消耗大量CPU資源。
Activity/Service生命周期如何在死循環(huán)外執(zhí)行的氓皱?ActivityThread的內(nèi)部類H繼承自Handler路召,通過Handler發(fā)送各種類型的消息(包括Activity/Service生命周期消息),死循環(huán)處理消息波材,就可以回調(diào)Activity/Service的生命周期方法股淡。
這里再用簡單的描述來總結(jié)這個問題:
Android是基于事件驅(qū)動,應(yīng)用啟動后會進(jìn)入一個死循環(huán)監(jiān)聽事件廷区,而Android中幾乎所有的系統(tǒng)調(diào)用都是通過主線程的Handler發(fā)送消息唯灵,然后回調(diào)handleMessage,如果退出死循環(huán)隙轻,就不能響應(yīng)事件了埠帕,應(yīng)用程序就會退出了。當(dāng)沒有Message的時候玖绿,主線程釋放CPU資源敛瓷,處于休眠狀態(tài)(Linux pipe/epoll機(jī)制)
Android消息機(jī)制總結(jié)
用一張圖來總結(jié)Handler消息機(jī)制: