原文借鑒自:http://www.reibang.com/p/02962454adf7
搞安卓的消息處理機(jī)制應(yīng)該是最基礎(chǔ)的了,但是其中原理的話還是很少有人能深入下去,最低拜讀了大神文章堂淡,文章地址在開頭芥牌。偶有心得在此記錄下來寸爆。
從App啟動開始替废,一開始找ActivityThread類,我用的是Mac版studio绎晃,在項(xiàng)目里面打上這個(gè)單詞蜜唾,竟然沒找到,Looper,handler能找到庶艾。找了一個(gè)大神問了問袁余,雙擊shift鍵出現(xiàn)搜索,然后打上這個(gè)單詞才找到咱揍。據(jù)大神的說泌霍,默認(rèn)的只是一部分常用的framework源碼,雙擊搜索的是sdk中的android源碼述召。
代碼如下:
public final class ActivityThread {
public static final void main(String[] args) {
......
Looper.prepareMainLooper();
......
ActivityThread thread = new ActivityThread();
thread.attach(false);
......
Looper.loop();
......
}
}
吐槽一下,這個(gè)main函數(shù)還是挺難找的蟹地,源碼里面是放到了類的最底部积暖,5300多行了。怪与。夺刑。
首先執(zhí)行Looper.prepareMainLooper(),這里面有個(gè)ThreadLocal 線程本地存儲區(qū)(Thread Local Storage分别,簡稱為TLS)遍愿,每個(gè)線程都有自己的私有的本地存儲區(qū)域,不同線程之間彼此不能訪問對方的TLS區(qū)域耘斩。這里線程自己的本地存儲區(qū)域存放是線程自己的Looper沼填。
可以先簡單理解它相當(dāng)于map,key是線程括授,value是Looper坞笙,那么你只要用當(dāng)前的線程就能通過sThreadLocal獲取當(dāng)前線程所屬的Looper岩饼。
Looper.prepareMainLooper()做的事件就是new了一個(gè)Looper實(shí)例并放入Looper類下面一個(gè)static的ThreadLocal<Looper> sThreadLocal靜態(tài)變量中,同時(shí)給sMainLooper賦值,給sMainLooper賦值是為了方便通過Looper.getMainLooper()快速獲取主線程的Looper薛夜,sMainLooper是主線程的Looper可能獲取會比較頻繁籍茧,避免每次都到 sThreadLocal 去查找獲取。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();}
在Looper的構(gòu)造函數(shù)中創(chuàng)建了消息隊(duì)列MessageQueue梯澜,并且讓Looper持有MessageQueue的引用寞冯。執(zhí)行完Looper.prepareMainLooper()
之后,主線程從普通線程轉(zhuǎn)成一個(gè)Looper線程晚伙。
ActivityThread的構(gòu)造函數(shù)并沒有做什么事只是初始化了資源管理器吮龄。
ActivityThread() {
mResourcesManager = ResourcesManager.getInstance();
}
thread.attach(false);會創(chuàng)建一個(gè)Binder線程,他是在主線程進(jìn)入無限循環(huán)之前創(chuàng)建撬腾,接收系統(tǒng)服務(wù)發(fā)送的消息如LAUNCH_ACTIVITY螟蝙,PAUSE_ACTIVITY等等通過handler傳遞給主線程。
我們知道一個(gè)線程是一段可執(zhí)行的代碼民傻,當(dāng)可執(zhí)行代碼執(zhí)行完成后胰默,線程生命周期便會終止,線程就會退出漓踢,那么做為App的主線程牵署,如果代碼段執(zhí)行完了會怎樣?喧半,那么就會出現(xiàn)App啟動后執(zhí)行一段代碼后就自動退出了奴迅,這是很不合理的。所以為了防止代碼段被執(zhí)行完挺据,只能在代碼中插入一個(gè)死循環(huán)取具,那么代碼就不會被執(zhí)行完,然后自動退出扁耐,怎么在在代碼中插入一個(gè)死循環(huán)呢暇检?那么Looper出現(xiàn)了,在主線程中調(diào)用Looper.prepare()...Looper.loop()就會變當(dāng)前線程變成Looper線程(可以先簡單理解:無限循環(huán)不退出的線程)婉称,Looper.loop()方法里面有一段死循環(huán)的代碼块仆,重要的就是這句:
Message msg = queue.next(); //獲取隊(duì)列中的下一條消息,可能會線程阻塞
簡單說如果在主線程的MessageQueue沒有消息時(shí)王暗,就會阻塞在loop的queue.next()方法里悔据,時(shí)候主線程會釋放CPU資源進(jìn)入休眠狀態(tài),直到有下個(gè)消息進(jìn)來時(shí)候就會喚醒主線程俗壹,在2.2 版本以前科汗,這套機(jī)制是用我們熟悉的線程的wait和notify 來實(shí)現(xiàn)的,之后的版本涉及到Linux pipe/epoll機(jī)制绷雏,通過往pipe管道寫端寫入數(shù)據(jù)來喚醒主線程工作肛捍。原理類似于I/O,讀寫是堵塞的隐绵,不占用CPU資源。
分析到這里拙毫,從應(yīng)用啟動創(chuàng)建Looper依许,創(chuàng)建消息隊(duì)列,到進(jìn)入loop方法執(zhí)行無限循環(huán)中缀蹄,那么這一塊就告一段落了峭跳,主線程已經(jīng)在死循環(huán)里輪詢等待消息了,接下來我們就要再看看缺前,系統(tǒng)是怎么發(fā)消息給主線程的蛀醉,主線程是怎么處理這些個(gè)消息的?
在準(zhǔn)備啟動一個(gè)Activity的時(shí)候衅码,系統(tǒng)服務(wù)進(jìn)程下的ActivityManagerService(簡稱AMS)線程會通過Binder發(fā)送IPC調(diào)用給APP進(jìn)程拯刁,App進(jìn)程接到到調(diào)用后,通過App進(jìn)程下的Binder線程最終調(diào)用ActivityThread類下面的scheduleLaunchActivity方法來準(zhǔn)備啟動Activity逝段,在Binder線程執(zhí)行mH.sendMessage(msg);垛玻,由主線程創(chuàng)建的Handler mH實(shí)例發(fā)送消息到主線程的消息隊(duì)列里,消息隊(duì)列從無到有奶躯,主線程堵塞被喚醒帚桩,主線程loop拿到消息,并回調(diào)mH的handleMessage 方法處理LAUNCH_ACTIVITY 等消息嘹黔。從而實(shí)現(xiàn)Activity的啟動账嚎。
1、Handler 對象在哪個(gè)線程下構(gòu)建(Handler的構(gòu)造函數(shù)在哪個(gè)線程下調(diào)用)儡蔓,那么Handler 就會持有這個(gè)線程的Looper引用和這個(gè)線程的消息隊(duì)列的引用郭蕉。因?yàn)槌钟羞@個(gè)線程的消息隊(duì)列的引用,意味著這個(gè)Handler對象可以在任意其他線程給該線程的消息隊(duì)列添加消息喂江,也意味著Handler的handlerMessage 肯定也是在該線程執(zhí)行的恳不。
2、如果該線程不是Looper線程开呐,在這個(gè)線程new Handler 就會報(bào)錯(cuò)!
Handler 有很多sendXXXX開頭的方法sendMessageAtTime规求、sendEmptyMessageDelayed筐付、sendEmptyMessage等等,都是用來給消息隊(duì)列添加消息的阻肿,那么這些方法最終都會調(diào)用enqueueMessage來實(shí)現(xiàn)消息進(jìn)入隊(duì)列瓦戚。
最后我們再看下Handler 的dispatchMessage方法,這個(gè)方法在Looper線程從消息隊(duì)列拿出來的時(shí)候,通過msg.target.dispatchMessage(msg)
調(diào)用的丛塌。