一、google為什么設(shè)計(jì)這套機(jī)制
主要是為了解決在非UI線程中更新UI組件比較麻煩的問(wèn)題。
二、google如何實(shí)現(xiàn)這套機(jī)制
UI線程中有一個(gè)線程專屬的Looper對(duì)象,它負(fù)責(zé)安排所有準(zhǔn)備在UI線程上執(zhí)行的代碼。這里有兩點(diǎn)技術(shù):實(shí)現(xiàn)UI線程專屬的Looper對(duì)象用到了java的ThreadLocal技術(shù),想深究請(qǐng)直接看ThreadLocal源碼,不難,真心不難,當(dāng)然不看也行,只要知道有這么回事也夠,需要時(shí)再看。Looper對(duì)象通過(guò)消息機(jī)制接受系統(tǒng)或者應(yīng)用的其他線程提交的準(zhǔn)備在UI線程上執(zhí)行的代碼。提交方式是:以該Looper對(duì)象為參數(shù)創(chuàng)建一個(gè)Handler對(duì)象,也可以在UI線程中無(wú)參數(shù)構(gòu)建一個(gè)Handler對(duì)象,此時(shí)的Handler對(duì)象直接就與UI線程的Looper對(duì)象綁定。Handler對(duì)象負(fù)責(zé)向Looper對(duì)象提交代碼。最直接的提交方法是調(diào)用Handler對(duì)象的post方法,該方法的參數(shù)是一個(gè)Runnable對(duì)象,代表了要在UI線程上執(zhí)行的代碼。這樣的方法簡(jiǎn)單但是沒(méi)法傳遞數(shù)據(jù)給要執(zhí)行的代碼,因?yàn)闃?gòu)造Runnable對(duì)象是沒(méi)有參數(shù)的。為此,google提供了另一種提交代碼的方法,就是讓Handler對(duì)象發(fā)送一個(gè)消息給Looper對(duì)象,這個(gè)消息中可以包含一定的數(shù)據(jù)(消息的what域和obj域就是用來(lái)包含數(shù)據(jù)的),要執(zhí)行的代碼就是Handler中的handleMessage方法,該方法會(huì)收到發(fā)送給Looper的消息,進(jìn)而可以從中取出數(shù)據(jù)再執(zhí)行代碼。
以上為原理。
三、一些可以幫助理解原理的細(xì)節(jié)
因?yàn)長(zhǎng)ooper是給UI線程安排代碼的,所以一個(gè)UI線程只能有一個(gè)Looper對(duì)象,否則多個(gè)Looper對(duì)象都要在UI線程上安排代碼,解決沖突就是個(gè)大難題。因?yàn)長(zhǎng)ooper對(duì)象是線程專屬的,所以一個(gè)Looper也只能對(duì)應(yīng)一個(gè)UI線程。二者是一對(duì)一的關(guān)系。但是一個(gè)Looper對(duì)象可以有多個(gè)Handler對(duì)象向它提交代碼,這并不會(huì)引起代碼沖突。因?yàn)長(zhǎng)ooper對(duì)象會(huì)線性安排在UI線程上待執(zhí)行的代碼,它通過(guò)一個(gè)隊(duì)列管理各個(gè)Handler對(duì)象提交的代碼。Looper對(duì)象安排執(zhí)行代碼靠的是它的loop方法。
四、具體使用方法
將一個(gè)與UI線程上Looper對(duì)象關(guān)聯(lián)的Handler對(duì)象傳給其他線程,其他線程通過(guò)這個(gè)對(duì)象向UI線程上提交代碼。
五、該機(jī)制的一般性擴(kuò)展
實(shí)際上,可以為任意一個(gè)線程創(chuàng)建一個(gè)唯一的Looper對(duì)象,這是通過(guò)Looper類的靜態(tài)方法prepare實(shí)現(xiàn)的,然后可以用這個(gè)Looper對(duì)象創(chuàng)建一個(gè)或多個(gè)Handler對(duì)象,然后就可以用這些Handler對(duì)象向該線程提交執(zhí)行代碼了,google提供了一個(gè)HandlerThread類,就是一個(gè)已經(jīng)實(shí)現(xiàn)好了Looper對(duì)象的Thread,方便你的使用。
1、我們先說(shuō)下什么是Android消息處理機(jī)制?
消息處理機(jī)制本質(zhì):一個(gè)線程開(kāi)啟循環(huán)模式持續(xù)監(jiān)聽(tīng)并依次處理其他線程給它發(fā)的消息。
簡(jiǎn)單的說(shuō):一個(gè)線程開(kāi)啟一個(gè)無(wú)限循環(huán)模式,不斷遍歷自己的消息列表,如果有消息就挨個(gè)拿出來(lái)做處理,如果列表沒(méi)消息,自己就堵塞(相當(dāng)于wait,讓出cpu資源給其他線程),其他線程如果想讓該線程做什么事,就往該線程的消息隊(duì)列插入消息,該線程會(huì)不斷從隊(duì)列里拿出消息做處理。
2、Android消息處理機(jī)制的工作原理?
打個(gè)比方:公司類比App
PM 的主要工作是設(shè)計(jì)產(chǎn)品,寫需求文檔,改需求,中途改需求,提測(cè)前改需求...
UI 主要工作是UI設(shè)計(jì),交互等。
RD 工作我就不說(shuō)了
CEO 不解釋。
公司開(kāi)創(chuàng)之后(App啟動(dòng)),那么CEO開(kāi)始干活了(主線程【UI線程】啟動(dòng)),這時(shí)候CEO開(kāi)啟了無(wú)限循環(huán)工作狂模式,自己的公司沒(méi)辦法?。ㄏ喈?dāng)于UI主線程轉(zhuǎn)成Looper線程【源碼里面有】)CEO招了一名RD(new Handler 實(shí)例)并把告訴PM和UI,如果你們有什么任務(wù)和需求就讓RD(Handler實(shí)例)轉(zhuǎn)告給我(CEO)。RD會(huì)把PM和UI的需求(Message)一條條記到CEO的備忘錄里(MessageQueue)。CEO 無(wú)限循環(huán)的工作就是不斷查看備忘錄,看有什么任務(wù)要做,有任務(wù)就從備忘錄一條一條拿出任務(wù)來(lái),然后交給這一名RD(Handler 實(shí)例)去處理(畢竟CEO 不會(huì)寫代碼 囧...)。當(dāng)然如果備忘錄都做完了,這時(shí)候CEO就會(huì)去睡覺(jué)(線程堵塞【簡(jiǎn)單理解成線程wait】,讓出CPU資源,讓其他線程去執(zhí)行)。但是這個(gè)備忘錄有個(gè)特殊的功能就是沒(méi)有任務(wù)的時(shí)候突然插入第一條任務(wù)(從無(wú)到有)就會(huì)有鬧鐘功能叫醒CEO起床繼續(xù)處理備忘錄。 整個(gè)消息處理機(jī)制的工作原理基本就是這樣的。后面會(huì)有源碼分析,你再來(lái)結(jié)合這個(gè)場(chǎng)景,會(huì)更好理解一些。
這里先給一張Android消息處理機(jī)制流程圖和具體執(zhí)行動(dòng)畫,如果看不懂沒(méi)事,接著往下看(后面會(huì)結(jié)合Android UI主線程來(lái)講解),然后結(jié)合著圖和動(dòng)畫一塊看更能理解整個(gè)機(jī)制的實(shí)現(xiàn)原理。
3、Looper、Handler、MessageQueue,Message作用和存在的意義?
Looper
我們知道一個(gè)線程是一段可執(zhí)行的代碼,當(dāng)可執(zhí)行代碼執(zhí)行完成后,線程生命周期便會(huì)終止,線程就會(huì)退出,那么做為App的主線程,如果代碼段執(zhí)行完了會(huì)怎樣?,那么就會(huì)出現(xiàn)App啟動(dòng)后執(zhí)行一段代碼后就自動(dòng)退出了,這是很不合理的。所以為了防止代碼段被執(zhí)行完,只能在代碼中插入一個(gè)死循環(huán),那么代碼就不會(huì)被執(zhí)行完,然后自動(dòng)退出,怎么在在代碼中插入一個(gè)死循環(huán)呢?那么Looper出現(xiàn)了,在主線程中調(diào)用Looper.prepare()...Looper.loop()就會(huì)變當(dāng)前線程變成Looper線程(可以先簡(jiǎn)單理解:無(wú)限循環(huán)不退出的線程),Looper.loop()方法里面有一段死循環(huán)的代碼,所以主線程會(huì)進(jìn)入while(true){...}的代碼段跳不出來(lái),但是主線程也不能什么都不做吧?其實(shí)所有做的事情都在while(true){...}里面做了,主線程會(huì)在死循環(huán)中不斷等其他線程給它發(fā)消息(消息包括:Activity啟動(dòng),生命周期,更新UI,控件事件等),一有消息就根據(jù)消息做相應(yīng)的處理,Looper的另外一部分工作就是在循環(huán)代碼中會(huì)不斷從消息隊(duì)列挨個(gè)拿出消息給主線程處理。
MessageQueue
MessageQueue 存在的原因很簡(jiǎn)單,就是同一線程在同一時(shí)間只能處理一個(gè)消息,同一線程代碼執(zhí)行是不具有并發(fā)性,所以需要隊(duì)列來(lái)保存消息和安排每個(gè)消息的處理順序。多個(gè)其他線程往UI線程發(fā)送消息,UI線程必須把這些消息保持到一個(gè)列表(它同一時(shí)間不能處理那么多任務(wù)),然后挨個(gè)拿出來(lái)處理,這種設(shè)計(jì)很簡(jiǎn)單,我們平時(shí)寫代碼其實(shí)也經(jīng)常這么做。每一個(gè)Looper線程都會(huì)維護(hù)這樣一個(gè)隊(duì)列,而且僅此一個(gè),這個(gè)隊(duì)列的消息只能由該線程處理。
Handler
簡(jiǎn)單說(shuō)Handler用于同一個(gè)進(jìn)程的線程間通信。Looper讓主線程無(wú)限循環(huán)地從自己的MessageQueue拿出消息處理,既然這樣我們就知道處理消息肯定是在主線程中處理的,那么怎樣在其他的線程往主線程的隊(duì)列里放入消息呢?其實(shí)很簡(jiǎn)單,我們知道在同一進(jìn)程中線程和線程之間資源是共享的,也就是對(duì)于任何變量在任何線程都是可以訪問(wèn)和修改的,只要考慮并發(fā)性做好同步就行了,那么只要拿到MessageQueue 的實(shí)例,就可以往主線程的MessageQueue放入消息,主線程在輪詢的時(shí)候就會(huì)在主線程處理這個(gè)消息。那么怎么拿到主線程 MessageQueue的實(shí)例,是可以拿到的(在主線程下mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),但是Google 為了統(tǒng)一添加消息和消息的回調(diào)處理,又專門構(gòu)建了Handler類,你只要在主線程構(gòu)建Handler類,那么這個(gè)Handler實(shí)例就獲取主線程MessageQueue實(shí)例的引用(獲取方式mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),Handler 在sendMessage的時(shí)候就通過(guò)這個(gè)引用往消息隊(duì)列里插入新消息。Handler 的另外一個(gè)作用,就是能統(tǒng)一處理消息的回調(diào)。這樣一個(gè)Handler發(fā)出消息又確保消息處理也是自己來(lái)做,這樣的設(shè)計(jì)非常的贊。具體做法就是在隊(duì)列里面的Message持有Handler的引用(哪個(gè)handler 把它放到隊(duì)列里,message就持有了這個(gè)handler的引用),然后等到主線程輪詢到這個(gè)message的時(shí)候,就來(lái)回調(diào)我們經(jīng)常重寫的Handler的handleMessage(Message msg)方法。
Message
Message 很簡(jiǎn)單了,你想讓主線程做什么事,總要告訴它吧,總要傳遞點(diǎn)數(shù)據(jù)給它吧,Message就是這個(gè)載體。
-------------------------------
剛好畫了UML圖,核心的幾個(gè)類如下:
Handler本質(zhì)是用于跨線程通信的,如果是“子線程執(zhí)行耗時(shí)操作,完畢后通知主線程”,就是我們常說(shuō)的異步過(guò)程;當(dāng)然也可以是兩個(gè)對(duì)等子線程,例如線程A與線程B,要實(shí)現(xiàn)線程A擁有消息循環(huán),線程B執(zhí)行執(zhí)行某項(xiàng)操作后通知線程A。
Android 異步消息處理機(jī)制 讓你深入理解 Looper、Handler、Message三者關(guān)系
這個(gè)過(guò)程可以這樣描述:
1、線程A中new一個(gè)Looper對(duì)象(同時(shí)也在其中new了MessageQueue對(duì)象),并封裝到線程的成員變量ThreadLocal.ThreadLocalMap中;
2、線程A中new一個(gè)Handler對(duì)象(new的過(guò)程中獲得當(dāng)前線程的Looper對(duì)象和其中的MessageQueue對(duì)象),覆寫其中的dispatchMessage(Message msg)函數(shù);
3、線程A中獲得Looper對(duì)象,并獲得其中的MessageQueue對(duì)象,然后進(jìn)入死循環(huán),不斷遍歷MessageQueue;
4、線程B實(shí)例化Message,并將target設(shè)置為線程A中的Handler,通過(guò)線程A的Handler對(duì)象,將Message放到Handler的MessageQueue對(duì)象中,此時(shí)線程A的死循環(huán)能獲得這個(gè)Message,然后執(zhí)行了這個(gè)Message的回調(diào),也就是Handler的dispatchMessage(Message msg)函數(shù)。
?
使用AWS構(gòu)建可靠且可擴(kuò)展的網(wǎng)站和Web應(yīng)用程序
在傳統(tǒng)數(shù)據(jù)中心開(kāi)發(fā)和部署可伸縮、全球可用的Web應(yīng)用需要執(zhí)行大量的手動(dòng)操作,這會(huì)耗費(fèi)大量人力物力,負(fù)載高峰和流量波動(dòng)會(huì)導(dǎo)致資源配置不足或過(guò)度配置。如今在AWS上構(gòu)建網(wǎng)站免費(fèi),在云中構(gòu)建、部署和管理網(wǎng)站和Web應(yīng)用程序所需的免費(fèi)產(chǎn)品和服務(wù)易如反掌。查看詳情
Android程序啟動(dòng)后會(huì)起一個(gè)進(jìn)程,所有的組件都在這個(gè)進(jìn)程里面運(yùn)行。開(kāi)始這個(gè)進(jìn)程只包含一個(gè)線程,叫做UI主線程,負(fù)責(zé)處理UI界面的顯示更新。對(duì)于一些費(fèi)時(shí)的操作(超過(guò)5S會(huì)卡頓)需要單獨(dú)啟動(dòng)一個(gè)子線程去處理。子線程處理完畢將結(jié)果通知給UI主線程,主線程得到結(jié)果后更新UI界面。子線程與UI主線程的通信在android中使用了消息機(jī)制來(lái)完成,那么是怎么完成的呢?這就和handler 機(jī)制的原理,簡(jiǎn)而言之言而總之,就是需要兩樣樣古老的東西,消息隊(duì)列、輪詢。也就是說(shuō),主線程起來(lái)以后有一個(gè)消息隊(duì)列,同時(shí)和該隊(duì)列配對(duì)的有一個(gè)輪詢,而子線程有這個(gè)消息隊(duì)列的引用,那這樣,子線程處理完以后就會(huì)向主線程的消息隊(duì)列發(fā)消息,主線程輪詢自己的隊(duì)列,發(fā)現(xiàn)有未處理的消息就進(jìn)行處理。這就是handler的機(jī)制,對(duì)于handler具體怎么設(shè)計(jì)的,不解釋,去查查String 的字符串池
Handler 是一個(gè)消息分發(fā)對(duì)象。而消息分發(fā),有賴于消息循環(huán),也就是 Looper。在一個(gè)線程中,Looper 阻塞線程,等待消息構(gòu)成循環(huán),有了消息,分配到對(duì)應(yīng)的 Handler,讓他進(jìn)一步分發(fā)處理,如是。
# 1. 什么是消息機(jī)制
說(shuō)到消息機(jī)制,作為一名 Android 開(kāi)發(fā)者一定先想到的是 Handler。Handler 就是 Android 消息機(jī)制的上層接口,我們可用通過(guò) Handler 輕松的在不同的線程中切換任務(wù),但 Handler 的實(shí)現(xiàn)還有兩個(gè)很重要的概念MessageQueue和Looper。
MessageQueue 的翻譯是消息隊(duì)列,它的內(nèi)部采用了單鏈表的結(jié)構(gòu)存儲(chǔ) Handler 對(duì)象發(fā)送的消息。
Looper 的作用是不斷地查詢 MessageQueue 中是否有消息,如果 Looper 發(fā)現(xiàn) MessageQueue 中存入了新的消息,它就會(huì)去處理這條消息,如果沒(méi)有新消息,Looper 就會(huì)以無(wú)限循環(huán)的方式去查詢 MessageQueue 中是否有新消息。
# 2. 為什么要有 Handler
## 2.1)官方文檔中 Handler 的主要作用
(1)安排將來(lái)某個(gè)時(shí)間點(diǎn)執(zhí)行的Message和Runnables;
(2)在不同于當(dāng)前的線程上執(zhí)行的操作;
## 2.2)Handler 被用來(lái)做的最多的一件事就是更新主線程的 UI。
在 Android 開(kāi)發(fā)中,默認(rèn)子線程是不可以更新 UI 的,這一點(diǎn)可以從 View 的最高層級(jí) ViewRootImpl 類中找到答案
void checkThread() {
? ? if (mThread != Thread.currentThread()) {
? ? ? ? throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");
? ? }
}
ViewRootImpl 類中的checkThread方法會(huì)在更新 UI 前被執(zhí)行,如果當(dāng)前線程不是主線程,就會(huì)拋出Only the original thread that created a view hierarchy can touch its views.的異常
## 2.3)那么 Android 為什么要設(shè)計(jì)為只能在主線程中更新 UI 呢?
Android 在子線程中更新 UI 是不安全的,如果多個(gè)子線程同時(shí)修改一個(gè)控件的數(shù)據(jù),后果是不可控的
如果給 UI 更新機(jī)制加鎖,會(huì)降低 UI 的訪問(wèn)效率,并且可能阻塞某些線程的執(zhí)行
# 3. Handler 的用法
## 3.1)在主線程中創(chuàng)建 Handler
通常,我們?cè)谥骶€程中創(chuàng)建 Handler 的寫法如下:
private Handler handler = new Handler(){
? ? @Override
? ? public void handleMessage(Message msg) {
? ? ? ? super.handleMessage(msg);
? ? }
};
但這樣寫,系統(tǒng)會(huì)這樣提示:
This Handler class should be static or leaks might occur (anonymous android.os.Handler)
這個(gè)Handler類應(yīng)該是靜態(tài)的,否則可能會(huì)發(fā)生泄漏
出現(xiàn)這個(gè)警告但原因是,Handler 在 Activity 中作為一個(gè)匿名內(nèi)部類來(lái)定義,它的內(nèi)部持有來(lái) Activity 的實(shí)例。當(dāng) Activity 被用戶關(guān)閉時(shí),因?yàn)?Handler 持有了 Activity 的引用,就造成了 Activity 無(wú)法被回收,從而導(dǎo)致了內(nèi)存泄漏。
因此,在這里推薦一種更加安全的寫法:
private static class MyHandler extends Handler{
? ? private WeakReference<Activity> weakReference;
? ? public MyHandler(Activity activity){
? ? ? ? weakReference = new WeakReference<>(activity);
? ? }
? ? @Override
? ? public void handleMessage(Message msg) {
? ? ? ? super.handleMessage(msg);
? ? ? ? switch (msg.what){
? ? ? ? ? ? ? ? case 0:? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? Toast.makeText(weakReference.get(),Thread.currentThread().getName(),Toast.LENGTH_SHORT).show();
? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? }
? ? }
}
private MyHandler handler = new MyHandler(this);
通過(guò)靜態(tài)內(nèi)部類的方式實(shí)現(xiàn)一個(gè) Handler,此時(shí)內(nèi)部類并不持有外部類對(duì)象的應(yīng)用,需要在內(nèi)部類的構(gòu)造方法內(nèi)增加一個(gè)外部類(Activity)的弱應(yīng)用。這樣,即使 Activity 被關(guān)閉,Activity 也能順利被回收。
onCreate() 中的代碼如下:
btn_0 = findViewById(R.id.btn_0);
btn_0.setOnClickListener(new View.OnClickListener() {
? ? @Override
? ? public void onClick(View v) {
? ? ? ? new Thread(){
? ? ? ? ? ? @Override
? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? super.run();
? ? ? ? ? ? ? ? Message message = Message.obtain();
? ? ? ? ? ? ? ? message.what = 0;
? ? ? ? ? ? ? ? handler.sendMessage(message);
? ? ? ? ? ? }
? ? ? ? }.start();
? ? }
});
這時(shí)候點(diǎn)擊按鈕的運(yùn)行效果如下:
## 3.2)在子線程中創(chuàng)建 Handler
在官方文檔中 Handler 的主要作用是在不同于當(dāng)前線程的線程中執(zhí)行操作,那么如何用 Handler 解決兩個(gè)子線程之間的通信呢?
請(qǐng)看代碼:
btn_1 = findViewById(R.id.btn_1);
btn_1.setOnClickListener(new View.OnClickListener() {
? ? @Override
? ? public void onClick(View v) {
? ? ? ? new Thread(){
? ? ? ? ? ? @Override
? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? super.run();
? ? ? ? ? ? ? ? Looper.prepare();
? ? ? ? ? ? ? ? handler = new MyHandler(MainActivity.this);
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? sleep(1000);
? ? ? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? Looper.loop();
? ? ? ? ? ? }
? ? ? ? }.start();
? ? ? ? new Thread(){
? ? ? ? ? ? @Override
? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? super.run();
? ? ? ? ? ? ? ? Message message = Message.obtain();
? ? ? ? ? ? ? ? message.what = 0;
? ? ? ? ? ? ? ? handler.sendMessage(message);
? ? ? ? ? ? }
? ? ? ? }.start();
? ? }
});
此時(shí)點(diǎn)擊按鈕:
可見(jiàn)當(dāng)前的處理線程已經(jīng)變成了子線程。
# 4. Handler 工作原理
如果細(xì)心的觀察代碼,可以看到在子線程中創(chuàng)建 Handler 的時(shí)候調(diào)用了Looper.prepare()和Looper.loop()兩個(gè)方法。這兩句代碼有什么用呢?
我們暫時(shí)可以把 Looper 理解為消息的管理者,它負(fù)責(zé)從 MessageQueue 中提取出消息,傳遞給 Handler 進(jìn)行處理,每一個(gè) Handler 都必須要有一個(gè) Looper,在 Handler 創(chuàng)建的時(shí)候,它會(huì)自動(dòng)使用當(dāng)前線程的 Looper,而Looper.prepare()的作用就是為當(dāng)前線程準(zhǔn)備一個(gè) Looper,Looper.loop()的作用是開(kāi)始查找當(dāng)前 MessageQueue 中是否有了新的消息。
這就是 Handler 工作的第一步 :
## 4.1)采用當(dāng)前線程的 Looper 創(chuàng)建 Handler
因?yàn)檫@里主要講 Handler 的工作流程,創(chuàng)建 Looper 的具體過(guò)程放到文章的下面講解。我們只要知道
Looper.prepare()為當(dāng)前的線程創(chuàng)建了一個(gè) Looper 對(duì)象即可。
但是,在主線程中創(chuàng)建 Handler 的時(shí)候,我們并沒(méi)有看到Looper.prepare()的執(zhí)行,這是因?yàn)樵?UI 線程,即 ActivityThread 的創(chuàng)建過(guò)程中,Looper 已經(jīng)被創(chuàng)建好了。
我們可以在 ActivityThread 的 main() 方法中看到這樣一句代碼:
Looper.prepareMainLooper();
這個(gè)方法內(nèi)部也調(diào)用了Looper.prepare()為 UI 線程創(chuàng)建了一個(gè) Looper。
## 4.2)通過(guò) Handler 的 `sendMessageAtTime()` 方法發(fā)送 Message
為什么是sendMessageAtTime?不是還有sendMessage(),sendEmptyMessage(),sendEmptyMessageDelayed(),sendEmptyMessageAtTime(),sendMessageDelayed()這么多方法嗎?
通過(guò)閱讀這些方法的源碼可以發(fā)現(xiàn),這些方法最終調(diào)用的都是sendMessageAtTime()。
其次還有post(),postAtTime(),postDelayed()方法最終調(diào)用的也都是sendMessageAtTime()方法,只是多了一步調(diào)用getPostMessage(Runnable r, Object token)將 Runnable 封裝為一個(gè) Message 對(duì)象的 callback 里。
public final boolean post(Runnable r){
? return? sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
? ? Message m = Message.obtain();
? ? m.callback = r;
? ? return m;
}
那么sendMessageAtTime()里的具體操作是什么呢?我們?nèi)ピ创a里一探究竟
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
? ? // 先獲取當(dāng)前 Handler 中的 MessageQueue,mQueue 在 Looper 的構(gòu)造方法中進(jìn)行初始化。
? ? MessageQueue queue = mQueue;
? ? if (queue == null) {
? ? ? ? RuntimeException e = new RuntimeException(
? ? ? ? ? ? ? ? this + " sendMessageAtTime() called with no mQueue");
? ? ? ? Log.w("Looper", e.getMessage(), e);
? ? ? ? return false;
? ? }
? ? // queue 不為空,則執(zhí)行 Handler.java 里的另一個(gè) enqueueMessage() 方法
? ? return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
? ? // 指定 msg 的 Target 對(duì)象為當(dāng)前的 Handler
? ? msg.target = this;
? ? if (mAsynchronous) {
? ? ? msg.setAsynchronous(true);
? ? }
? ? return queue.enqueueMessage(msg, uptimeMillis);
}
Handler 中的 enqueueMessage() ,最終會(huì)調(diào)用 MessageQueue.java 中的 enqueueMessage() 方法。
之后,Message 對(duì)象最終傳遞到 MessageQueue 即消息隊(duì)列里中,在消息隊(duì)列里的具體處理邏輯在文章的MessageQueue 工作原理部分會(huì)具體解釋。
## 4.3)Looper 處理消息后調(diào)用 Handler 的 dispatchMessage() 方法
在第二步將消息插入消息隊(duì)列后,Looper 就開(kāi)始遍歷消息隊(duì)列,找到新的消息,再通知 Handler 去執(zhí)行這條消息,調(diào)用的就是 Handler 的dispatchMessage()方法。
public void dispatchMessage(Message msg) {
? // msg 的 callback 對(duì)象就是一個(gè) Runnable
? if (msg.callback != null) {
? ? ? ? handleCallback(msg);
? ? } else {
? ? ? ? // 檢查 mCallback 是否為空,不為空就執(zhí)行它內(nèi)部定義的 handleMessage() 方法
? ? ? ? if (mCallback != null) {
? ? ? ? ? ? if (mCallback.handleMessage(msg)) {
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? // 如果 mCallback 為空,就執(zhí)行在實(shí)例化 Handler 過(guò)程中我們自己定義的 handleMessage() 方法中的內(nèi)容
? ? ? ? handleMessage(msg);
? ? }
}
dispatchMessage()方法首先會(huì)檢查 Message 的 Callback 對(duì)象是否為空,callback 就是通過(guò) post() 方法傳遞的 Runnable 對(duì)象,如果 callback 不為空,就去執(zhí)行 handleCallback() 方法。
handleCallback() 方法的實(shí)現(xiàn)也很簡(jiǎn)單,它在內(nèi)部執(zhí)行了 Runnable 的run()方法
private static void handleCallback(Message message) {
? ? message.callback.run();
}
如果 callback 對(duì)象為空,就檢查 mCallback 是否為空,不為空就執(zhí)行它的定義的? handleMessage() 方法,若沒(méi)有 mCallback,最終將直接執(zhí)行我們?cè)诶^承 Handler 時(shí)自己定義的 handleMessage() 方法中的代碼。
Callback是 Handler 中定義的的一個(gè)接口,它的代碼如下:
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*/
public interface Callback {
? ? /**
? ? * @param msg A {@link android.os.Message Message} object
? ? * @return True if no further handling is desired
? ? */
? ? public boolean handleMessage(Message msg);
}
如果使用 Callback 接口的話,我們可以直接實(shí)例化一個(gè) Handler 而不用去實(shí)現(xiàn)一個(gè) Handler 的子類,
private Handler mHandler = new Handler(new Handler.Callback() {
? ? @Override
? ? public boolean handleMessage(Message msg) {
? ? ? ? return false;
? ? }
});
# 5. MessageQueue 工作原理
我們從上一部分的 MessageQueue.java 中的 enqueueMessage() 方法開(kāi)始入手。
## 5.1)enqueueMessage()
代碼量有點(diǎn)多,要耐心看哦!
boolean enqueueMessage(Message msg, long when) {
? ? // 檢查當(dāng)前 msg 的 target 是否為空
? ? if (msg.target == null) {
? ? ? ? throw new IllegalArgumentException("Message must have a target.");
? ? }
? ? // msg 如果正在被執(zhí)行,就拋出異常
? ? if (msg.isInUse()) {
? ? ? ? throw new IllegalStateException(msg + " This message is already in use.");
? ? }
? ? synchronized (this) {
? ? ? ? // 在 quit() 方法中,mQuitting 會(huì)被設(shè)置為 true
? ? ? ? if (mQuitting) {
? ? ? ? ? ? IllegalStateException e = new IllegalStateException(
? ? ? ? ? ? ? ? ? ? msg.target + " sending message to a Handler on a dead thread");
? ? ? ? ? ? Log.w(TAG, e.getMessage(), e);
? ? ? ? ? ? msg.recycle();
? ? ? ? ? ? return false;
? ? ? ? }
? ? ? ? // 標(biāo)記當(dāng)前的 msg 正在執(zhí)行
? ? ? ? msg.markInUse();
? ? ? ? // 設(shè)置 msg 的 when 為傳進(jìn)來(lái)的 when 參數(shù),when 是 Message 想要被執(zhí)行的時(shí)間
? ? ? ? msg.when = when;
? ? ? ? // 得到當(dāng)前消息隊(duì)列的頭部消息
? ? ? ? Message p = mMessages;
? ? ? ? boolean needWake;
? ? ? ? // 當(dāng)前消息隊(duì)列為空,新消息的觸發(fā)時(shí)間為 0,或者新消息的觸發(fā)時(shí)間早于消息中第一條消息的觸發(fā)時(shí)間
? ? ? ? // 則將新消息插入到隊(duì)列的頭部,作為當(dāng)前消息隊(duì)列的第一條消息
? ? ? ? if (p == null || when == 0 || when < p.when) {
? ? ? ? ? ? // New head, wake up the event queue if blocked.
? ? ? ? ? ? // 將當(dāng)前消息的下一條消息指向頭部消息
? ? ? ? ? ? msg.next = p;
? ? ? ? ? ? // 頭部消息修改為當(dāng)前消息
? ? ? ? ? ? mMessages = msg;
? ? ? ? ? ? // 當(dāng)阻塞時(shí),需要喚醒
? ? ? ? ? ? needWake = mBlocked;
? ? ? ? } else {
? ? ? ? ? ? // 將新消息插入到當(dāng)前消息隊(duì)列當(dāng)中,(不是頭部)
? ? ? ? ? ? // 通常我們不必喚醒事件隊(duì)列,
? ? ? ? ? ? // 除非隊(duì)列頭部有消息障礙,并且消息是隊(duì)列中最早的異步消息。
? ? ? ? ? ? needWake = mBlocked && p.target == null && msg.isAsynchronous();
? ? ? ? ? ? Message prev;
? ? ? ? ? ? // 開(kāi)始循環(huán)便利消息隊(duì)列,比較新消息和隊(duì)列中消息的 when(觸發(fā)事件)的值,將新消息插入到適當(dāng)位置
? ? ? ? ? ? for (;;) {
? ? ? ? ? ? ? ? // 循環(huán)第一次遍歷時(shí),將當(dāng)前隊(duì)列中的頭部消息賦值給 prev
? ? ? ? ? ? ? ? prev = p;
? ? ? ? ? ? ? ? // p 指向隊(duì)列中的第二個(gè)消息
? ? ? ? ? ? ? ? p = p.next;
? ? ? ? ? ? ? ? // 如果下一個(gè)消息為空,或者新消息的觸發(fā)時(shí)間早于下一個(gè)消息,找到了要插入的位置,退出循環(huán)
? ? ? ? ? ? ? ? if (p == null || when < p.when) {
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? // needWake 為 true,并且 下一條消息是異步的,則不需要喚醒。
? ? ? ? ? ? ? ? if (needWake && p.isAsynchronous()) {
? ? ? ? ? ? ? ? ? ? needWake = false;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? // 將新消息插入到 p 之前,頭消息之后。
? ? ? ? ? ? msg.next = p; // invariant: p == prev.next
? ? ? ? ? ? prev.next = msg;
? ? ? ? }
? ? ? ? // 如果需要喚醒,調(diào)用 nativeWake 方法去喚醒
? ? ? ? if (needWake) {
? ? ? ? ? ? nativeWake(mPtr);
? ? ? ? }
? ? }
? ? return true;
}
執(zhí)行完 enqueueMassage 方法,我們新發(fā)送的 Message 就成功的插入了消息隊(duì)列當(dāng)中。
但是除了插入新消息,我們還需要從消息隊(duì)列中讀取消息,這又要怎么做呢?
## 5.2)next()
Message next() {
? ? // 如果消息循環(huán)已退出,并且被丟棄,則返回空。
? ? // 這個(gè)將在應(yīng)用重啟一個(gè) looper 時(shí)發(fā)生
? ? final long ptr = mPtr;
? ? if (ptr == 0) {
? ? ? ? return null;
? ? }
? ? // 記錄空閑時(shí)處理的 IdlerHandler 數(shù)量,只在第一次迭代時(shí)為 -1
? ? // IdleHandler 只在隊(duì)列為空 或者 是頭部消息時(shí)執(zhí)行
? ? int pendingIdleHandlerCount = -1;
? ? //? native 層使用的變量,設(shè)置的阻塞超時(shí)時(shí)長(zhǎng),0 為不阻塞,-1 為阻塞
? ? int nextPollTimeoutMillis = 0;
? ? for (;;) {
? ? ? ? if (nextPollTimeoutMillis != 0) {
? ? ? ? ? ? Binder.flushPendingCommands();
? ? ? ? }
? ? ? ? nativePollOnce(ptr, nextPollTimeoutMillis);
? ? ? ? // 嘗試檢索下一條消息。 如果找到則返回。
? ? ? ? synchronized (this) {
? ? ? ? ? ? // 獲取系統(tǒng)從開(kāi)機(jī)到現(xiàn)在到時(shí)間
? ? ? ? ? ? final long now = SystemClock.uptimeMillis();
? ? ? ? ? ? Message prevMsg = null;
? ? ? ? ? ? // 將隊(duì)列中到頭部消息賦值給 msg
? ? ? ? ? ? Message msg = mMessages;
? ? ? ? ? ? if (msg != null && msg.target == null) {
? ? ? ? ? ? ? ? // msg 不為空,但是這個(gè) msg 沒(méi)有 handler,則這個(gè) msg 為柵欄
? ? ? ? ? ? ? ? // 開(kāi)始遍歷,指到獲取第一個(gè)異步消息
? ? ? ? ? ? ? ? do {
? ? ? ? ? ? ? ? ? ? prevMsg = msg;
? ? ? ? ? ? ? ? ? ? msg = msg.next;
? ? ? ? ? ? ? ? } while (msg != null && !msg.isAsynchronous());
? ? ? ? ? ? }
? ? ? ? ? ? if (msg != null) {
? ? ? ? ? ? ? ? // 如果當(dāng)前時(shí)間不到 msg 的觸發(fā)時(shí)間,則計(jì)算時(shí)間差,設(shè)置阻塞超時(shí)時(shí)長(zhǎng)
? ? ? ? ? ? ? ? if (now < msg.when) {
? ? ? ? ? ? ? ? ? ? // Next message is not ready.? Set a timeout to wake up when it is ready.
? ? ? ? ? ? ? ? ? ? nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? // 當(dāng)前時(shí)間到了 msg 的觸發(fā)時(shí)間,則獲取消息并返回
? ? ? ? ? ? ? ? ? ? mBlocked = false;
? ? ? ? ? ? ? ? ? ? // 如果當(dāng)前的 msg 不是頭部消息,則上一條消息的 next 指向 msg 的 next
? ? ? ? ? ? ? ? ? ? if (prevMsg != null) {
? ? ? ? ? ? ? ? ? ? ? ? prevMsg.next = msg.next;
? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? // 當(dāng)前 msg 為頭部消息,則將下一個(gè) msg 設(shè)置為頭部消息
? ? ? ? ? ? ? ? ? ? ? ? mMessages = msg.next;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? // msg 的下一個(gè) Message 對(duì)象置空,表示從消息隊(duì)列中取出來(lái)了這條 msg
? ? ? ? ? ? ? ? ? ? msg.next = null;
? ? ? ? ? ? ? ? ? ? if (DEBUG) Log.v(TAG, "Returning message: " + msg);
? ? ? ? ? ? ? ? ? ? // 標(biāo)記 msg 正在使用
? ? ? ? ? ? ? ? ? ? msg.markInUse();
? ? ? ? ? ? ? ? ? ? return msg;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? // 如果沒(méi)有消息,則設(shè)置阻塞時(shí)長(zhǎng)為 -1,直到被喚醒
? ? ? ? ? ? ? ? nextPollTimeoutMillis = -1;
? ? ? ? ? ? }
? ? ? ? ? ? // 所有的消息都被處理后,判斷是否退出,并返回 null。
? ? ? ? ? ? if (mQuitting) {
? ? ? ? ? ? ? ? dispose();
? ? ? ? ? ? ? ? return null;
? ? ? ? ? ? }
? ? ? ? ? ? // If first time idle, then get the number of idlers to run.
? ? ? ? ? ? // Idle handles only run if the queue is empty or if the first message
? ? ? ? ? ? // in the queue (possibly a barrier) is due to be handled in the future.
? ? ? ? ? ? // 第一次循環(huán)時(shí),消息隊(duì)列為空,或 當(dāng)前時(shí)間未到消息的觸發(fā)時(shí)間,獲取 IdleHandler 的數(shù)量
? ? ? ? ? ? if (pendingIdleHandlerCount < 0
? ? ? ? ? ? ? ? ? ? && (mMessages == null || now < mMessages.when)) {
? ? ? ? ? ? ? ? pendingIdleHandlerCount = mIdleHandlers.size();
? ? ? ? ? ? }
? ? ? ? ? ? // pendingIdleHandlerCount 的數(shù)量為 0 時(shí),線程會(huì)繼續(xù)堵塞
? ? ? ? ? ? if (pendingIdleHandlerCount <= 0) {
? ? ? ? ? ? ? ? // No idle handlers to run.? Loop and wait some more.
? ? ? ? ? ? ? ? mBlocked = true;
? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? }
? ? ? ? ? ? // 判斷當(dāng)前空閑時(shí)處理任務(wù)的handler是否是為空,如果為空,就實(shí)例化出新的對(duì)象
? ? ? ? ? ? if (mPendingIdleHandlers == null) {
? ? ? ? ? ? ? ? mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
? ? ? ? ? ? }
? ? ? ? ? ? mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
? ? ? ? }
? ? ? ? // 運(yùn)行 IdleHandler,只有第一次循環(huán)時(shí)才會(huì)運(yùn)行
? ? ? ? for (int i = 0; i < pendingIdleHandlerCount; i++) {
? ? ? ? ? ? final IdleHandler idler = mPendingIdleHandlers[i];
? ? ? ? ? ? // 釋放 IdleHandler 的引用
? ? ? ? ? ? mPendingIdleHandlers[i] = null;
? ? ? ? ? ? boolean keep = false;
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? // 執(zhí)行 IdleHandler 的方法
? ? ? ? ? ? ? ? keep = idler.queueIdle();
? ? ? ? ? ? } catch (Throwable t) {
? ? ? ? ? ? ? ? Log.wtf(TAG, "IdleHandler threw exception", t);
? ? ? ? ? ? }
? ? ? ? ? ? if (!keep) {
? ? ? ? ? ? ? ? synchronized (this) {
? ? ? ? ? ? ? ? ? ? mIdleHandlers.remove(idler);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? // 重置 IdleHandler 的數(shù)量為 0,確保不會(huì)重復(fù)運(yùn)行它們
? ? ? ? pendingIdleHandlerCount = 0;
? ? ? ? // 在執(zhí)行 IdleHandler 時(shí),一個(gè)新的消息可能插入或消息隊(duì)列中的消息到了觸發(fā)時(shí)間
? ? ? ? // 所以將 nextPollTimeoutMillis 設(shè)為 0,表示不需要阻塞,重新檢查消息隊(duì)列。
? ? ? ? nextPollTimeoutMillis = 0;
? ? }
}
至此,MessageQueue 的兩個(gè)最重要的方法已經(jīng)分析完了,下面來(lái)看 Looper 如何循環(huán)地從消息隊(duì)列中取出消息。
# 6. Looper 工作原理
在講 Looper 之前,需要先理解 ThreadLocal 的工作原理
## 6.1)ThreadLocal 的工作原理
ThreadLocal 是一個(gè)線程內(nèi)存儲(chǔ)數(shù)據(jù)的類,當(dāng)不同的線程去訪問(wèn)同一個(gè) ThreadLocal 對(duì)象時(shí),獲得的值都是不一樣的,下面用一段代碼來(lái)證明
private ThreadLocal<String> mThreadLocal = new ThreadLocal<>();
btn_1 = findViewById(R.id.btn_1);
btn_1.setOnClickListener(new View.OnClickListener() {
? ? @Override
? ? public void onClick(View v) {
? ? ? ? new Thread(){
? ? ? ? ? ? @Override
? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? super.run();
? ? ? ? ? ? ? ? mThreadLocal.set("Thread_A");
? ? ? ? ? ? ? ? Log.d("ThreadLocalValue",mThreadLocal.get());
? ? ? ? ? ? }
? ? ? ? }.start();
? ? ? ? new Thread(){
? ? ? ? ? ? @Override
? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? super.run();
? ? ? ? ? ? ? ? mThreadLocal.set("Thread_B");
? ? ? ? ? ? ? ? Log.d("ThreadLocalValue",mThreadLocal.get());
? ? ? ? ? ? }
? ? ? ? }.start();? ? ? ?
? ? }
);
我在兩個(gè)線程中分別存入在 mThreadLocal 中存入了不同的值,然后在控制臺(tái)輸出它們的內(nèi)容
可見(jiàn)不同線程訪問(wèn)同一個(gè) ThreadLocal 對(duì)象得到的值也是不一樣的。
ThreadLocal 實(shí)現(xiàn)這種特性的原因也很簡(jiǎn)單,下面來(lái)看它內(nèi)部的 set 方法:
public void set(T value) {
? ? // 獲取當(dāng)前線程 t
? ? Thread t = Thread.currentThread();
? ? // 根據(jù)當(dāng)前線程 t,獲取當(dāng)前線程的 ThreadLocalMap 對(duì)象
? ? ThreadLocalMap map = getMap(t);
? ? if (map != null)
? ? ? ? // map 不為空,調(diào)用 ThreadLocalMap 的 set() 方法。
? ? ? ? map.set(this, value);
? ? else
? ? ? ? // map 為空,則為當(dāng)前線程創(chuàng)建一個(gè)新的 ThreadLocalMap 對(duì)象
? ? ? ? createMap(t, value);
}
在 set 方法中,先獲取當(dāng)前線程,然后獲取當(dāng)前線程的 ThreadLocalMap 對(duì)象。getMap() 的 和 createMap() 的實(shí)現(xiàn)如下:
ThreadLocalMap getMap(Thread t) {
? ? return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
? ? t.threadLocals = new ThreadLocalMap(this, firstValue);
}
那么 ThreadLocalMap 又是什么呢,這里是它的一部分源碼:
static class ThreadLocalMap {
? ? static class Entry extends WeakReference<ThreadLocal<?>> {
? ? ? ? /** The value associated with this ThreadLocal. */
? ? ? ? Object value;
? ? ? ? Entry(ThreadLocal<?> k, Object v) {
? ? ? ? ? ? super(k);
? ? ? ? ? ? value = v;
? ? ? ? }
? ? }
? ? // 初始的 table 容量
? ? private static final int INITIAL_CAPACITY = 16;
? ? // Entry 數(shù)組用于存儲(chǔ)數(shù)據(jù)
? ? private Entry[] table;
? ? // table 的大小
? ? private int size = 0;
? ? // 負(fù)載因子,用于擴(kuò)容
? ? private int threshold; // Default to 0
? ? // 設(shè)置負(fù)載因子為當(dāng)然容量大小的 2 / 3
? ? private void setThreshold(int len) {
? ? ? ? threshold = len * 2 / 3;
? ? }
? ? // 初始化 Entry 數(shù)組
? ? ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
? ? ? ? table = new Entry[INITIAL_CAPACITY];
? ? ? ? int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
? ? ? ? table[i] = new Entry(firstKey, firstValue);
? ? ? ? size = 1;
? ? ? ? setThreshold(INITIAL_CAPACITY);
? ? }
}
可以將 ThreadLocalMap 當(dāng)作一個(gè)哈希表,它的內(nèi)部用 Entry 存儲(chǔ)相應(yīng)的數(shù)據(jù)。
在 Thread 的屬性中有ThreadLocal.ThreadLocalMap threadLocals = null;,所以每一個(gè)線程內(nèi)部,都持有一個(gè) ThreadLocalMap 對(duì)象,系統(tǒng)才可以通過(guò)getMap()方法獲取當(dāng)前線程的 ThreadLocalMap 對(duì)象。
在 ThreadLocal 中調(diào)用 set 方法,實(shí)際上會(huì)調(diào)用 ThreadLocalMap 中的 set 方法,源碼如下:
// ThreadLocalMap 的 set 方法
private void set(ThreadLocal<?> key, Object value) {
? ? // We don't use a fast path as with get() because it is at
? ? // least as common to use set() to create new entries as
? ? // it is to replace existing ones, in which case, a fast
? ? // path would fail more often than not.
? ? // 首先獲取當(dāng)前 ThreadLocal 對(duì)象的 table 屬性,table 一個(gè) Entry 的數(shù)組
? ? // Entry 相當(dāng)于一個(gè) HashMap,存儲(chǔ)了當(dāng)前 ThreadLocal 對(duì)象和 Object 類型的 value 對(duì)象
? ? Entry[] tab = table;
? ? int len = tab.length;
? ? // 計(jì)算出存儲(chǔ)的位置
? ? int i = key.threadLocalHashCode & (len-1);
? ? // 遍歷 tab
? ? for (Entry e = tab[i];
? ? ? ? e != null;
? ? ? ? e = tab[i = nextIndex(i, len)]) {
? ? ? ? ThreadLocal<?> k = e.get();
? ? ? ? // 如果 tab 中已經(jīng)存在了相同的 key 值,就覆蓋它原有的 value
? ? ? ? if (k == key) {
? ? ? ? ? ? e.value = value;
? ? ? ? ? ? return;
? ? ? ? }
? ? ? ? // 如果 當(dāng)前 entrt 的 key 為 null,調(diào)用 replaceStaleEntry 方法清楚所有 key 為 null 的數(shù)據(jù)
? ? ? ? if (k == null) {
? ? ? ? ? ? replaceStaleEntry(key, value, i);
? ? ? ? ? ? return;
? ? ? ? }
? ? }
? ? ? ? // 都不滿足,就新建一個(gè) Entry 對(duì)象
? ? tab[i] = new Entry(key, value);
? ? int sz = ++size;
? ? // ThreadLocalMap 的容量到達(dá)閥值后擴(kuò)容
? ? if (!cleanSomeSlots(i, sz) && sz >= threshold)
? ? ? ? rehash();
}
ThreadLocal 中的 get() 方法和 set() 方法一樣,都是對(duì) Thread 中對(duì) ThreadLocalMap 進(jìn)行操作
public T get() {
? ? // 獲取當(dāng)前線程
? ? Thread t = Thread.currentThread();
? ? // 獲取當(dāng)前線程的 ThreadLocalMap 對(duì)象
? ? ThreadLocalMap map = getMap(t);
? ? if (map != null) {
? ? ? ? // 獲取 ThreadLocalMap 中對(duì)應(yīng)當(dāng)前線程的 Entry 對(duì)象
? ? ? ? ThreadLocalMap.Entry e = map.getEntry(this);
? ? ? ? if (e != null) {
? ? ? ? ? ? @SuppressWarnings("unchecked")
? ? ? ? ? ? // 將 Entry 對(duì)象中的 value 取出來(lái)
? ? ? ? ? ? T result = (T)e.value;
? ? ? ? ? ? return result;
? ? ? ? }
? ? }
? ? return setInitialValue();
}
private Entry getEntry(ThreadLocal<?> key) {
? ? int i = key.threadLocalHashCode & (table.length - 1);
? ? Entry e = table[i];
? ? if (e != null && e.get() == key)
? ? ? ? return e;
? ? else
? ? ? ? return getEntryAfterMiss(key, i, e);
}
## 6.2)Looper 中的 prepare() 方法
那么 ThreadLocal 和 Looper 有什么關(guān)系呢?我們知道每一個(gè)線程都有自己的 Looper,Looper 的作用域就是當(dāng)前的線程,Android 系統(tǒng)中便通過(guò) ThreadLocal 對(duì)象來(lái)存儲(chǔ)不同線程中的 Looper。
Looper 中 prepare() 方法為當(dāng)前線程創(chuàng)建一個(gè) Looper 對(duì)象,我們看一下它的實(shí)現(xiàn):
public static void prepare() {
? ? prepare(true);
}
private static void prepare(boolean quitAllowed) {
? ? if (sThreadLocal.get() != null) {
? ? ? ? throw new RuntimeException("Only one Looper may be created per thread");
? ? }
? ? // 將 Looper 對(duì)象保存到當(dāng)前線程的 ThreadLocalMap 當(dāng)中
? ? sThreadLocal.set(new Looper(quitAllowed));
}
這里再看一下 Looper 的構(gòu)造方法
private Looper(boolean quitAllowed) {
? ? mQueue = new MessageQueue(quitAllowed);
? ? mThread = Thread.currentThread();
}
可以看到在一個(gè) Looper 中創(chuàng)建了一個(gè) MessageQueue,這里我們就可以搞清楚 Handler、Looper 和 MessageQueue 的對(duì)應(yīng)關(guān)系了:
每個(gè)線程都有一個(gè) Looper 對(duì)象,在 Looper 對(duì)象的初始化過(guò)程中,會(huì)為當(dāng)前線程創(chuàng)建一個(gè) MessageQueue,而一個(gè)線程中可以有多個(gè) Handler。
## 6.3)Looper 中的 loop() 方法:
prepare() 調(diào)用后,就是調(diào)用 loop() 方法:
/**
? * Run the message queue in this thread. Be sure to call
? * {@link #quit()} to end the loop.
? */
public static void loop() {
? ? // 通過(guò) Thread Local 獲取當(dāng)前線程的 Looper
? ? final Looper me = myLooper();
? ? if (me == null) {
? ? ? ? throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
? ? }
? ? // 獲取當(dāng)前 Looper 對(duì)象的 MessageQueue
? ? final MessageQueue queue = me.mQueue;
? ? // 清空遠(yuǎn)程調(diào)用端進(jìn)程的身份,確保此線程的身份是本地進(jìn)程的身份,并跟蹤該身份令牌
? ? // 這里主要用于保證消息處理是發(fā)生在當(dāng)前 Looper 所在的線程
? ? Binder.clearCallingIdentity();
? ? final long ident = Binder.clearCallingIdentity();
? ? for (;;) {
? ? ? ? // 取出來(lái)下一條消息
? ? ? ? Message msg = queue.next(); // might block
? ? ? ? if (msg == null) {
? ? ? ? ? ? // No message indicates that the message queue is quitting.
? ? ? ? ? ? return;
? ? ? ? }
? ? ? ? // 用 logging 打印日志,默認(rèn)為 null,可通過(guò) setMessageLogging() 方法來(lái)指定
? ? ? ? final Printer logging = me.mLogging;
? ? ? ? if (logging != null) {
? ? ? ? ? ? logging.println(">>>>> Dispatching to " + msg.target + " " +
? ? ? ? ? ? ? ? ? ? msg.callback + ": " + msg.what);
? ? ? ? }
? ? ? ? // 開(kāi)始跟蹤,并寫入跟蹤消息,用于 debug 功能
? ? ? ? final long traceTag = me.mTraceTag;
? ? ? ? if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
? ? ? ? ? ? Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
? ? ? ? }? ? ? ?
? ? ? ? ...
? ? ? ? ...
? ? ? ? try {
? ? ? ? ? ? // // 通過(guò) Handler 分發(fā)消息
? ? ? ? ? ? msg.target.dispatchMessage(msg);
? ? ? ? ? ? dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
? ? ? ? } finally {
? ? ? ? ? ? if (traceTag != 0) {
? ? ? ? ? ? ? ? // 停止跟蹤
? ? ? ? ? ? ? ? Trace.traceEnd(traceTag);
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? if (logSlowDispatch) {
? ? ? ? ? ? showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
? ? ? ? }
? ? ? ? if (logging != null) {
? ? ? ? ? ? logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
? ? ? ? }
? ? ? ? //? 確保在分發(fā)消息的過(guò)程中線程的身份沒(méi)有改變
? ? ? ? final long newIdent = Binder.clearCallingIdentity();
? ? ? ? if (ident != newIdent) {
? ? ? ? ? ? Log.wtf(TAG, "Thread identity changed from 0x"
? ? ? ? ? ? ? ? ? ? + Long.toHexString(ident) + " to 0x"
? ? ? ? ? ? ? ? ? ? + Long.toHexString(newIdent) + " while dispatching to "
? ? ? ? ? ? ? ? ? ? + msg.target.getClass().getName() + " "
? ? ? ? ? ? ? ? ? ? + msg.callback + " what=" + msg.what);
? ? ? ? }
? ? ? ? // 回收消息,并把消息放入消息池
? ? ? ? msg.recycleUnchecked();
? ? }
}
可以看到 loop() 方法就是不停的遍歷消息隊(duì)列中的消息,當(dāng)發(fā)現(xiàn)有新的消息時(shí),便調(diào)用 Handler 的dispatchMessage()方法。
## 6.4)getMainLooper()
public static void prepareMainLooper() {
? ? prepare(false);
? ? synchronized (Looper.class) {
? ? ? ? if (sMainLooper != null) {
? ? ? ? ? ? throw new IllegalStateException("The main Looper has already been prepared.");
? ? ? ? }
? ? ? ? sMainLooper = myLooper();
? ? }
}
? /**
? ? * Returns the application's main looper, which lives in the main thread of the application.
? ? */
public static Looper getMainLooper() {
? ? synchronized (Looper.class) {
? ? ? ? return sMainLooper;
? ? }
}
getMainLooper() 方法用于返回當(dāng)前 UI 線程的 Looper,UI 線程的 Looper 在 ActivityThread 的建立時(shí)通過(guò)調(diào)用
prepareMainLooper()方法創(chuàng)建。
## 6.5)quit() 和 quitSafely()
在子線程中,如果手動(dòng)為其創(chuàng)建了Looper,那么在所有消息處理完成之后應(yīng)該調(diào)用 quit() 方法終止消息循環(huán),不然 Looper 就會(huì)一直處于等待狀態(tài)。
public void quitSafely() {
? ? mQueue.quit(true);
}
public void quit() {
? ? mQueue.quit(false);
}
可以看到這兩個(gè)方法都調(diào)用了 MessageQueue 中都 quit(boolean safe) 方法,quitSafely 的參數(shù)為 true,quit 的參數(shù)為 false。
void quit(boolean safe) {
? ? // 主線程不退出消息循環(huán)
? ? if (!mQuitAllowed) {
? ? ? ? throw new IllegalStateException("Main thread not allowed to quit.");
? ? }
? ? synchronized (this) {
? ? ? ? // 如果已經(jīng)退出了,直接 return
? ? ? ? if (mQuitting) {
? ? ? ? ? ? return;
? ? ? ? }
? ? ? ? // 標(biāo)記為已經(jīng)退出
? ? ? ? mQuitting = true;
? ? ? ? // 如果 safe 的值為 true,執(zhí)行完當(dāng)前的消息后退出消息循環(huán)
? ? ? ? if (safe) {
? ? ? ? ? ? removeAllFutureMessagesLocked();
? ? ? ? } else {
? ? ? ? ? ? // 直接退出消息循環(huán)
? ? ? ? ? ? removeAllMessagesLocked();
? ? ? ? }
? ? ? ? // We can assume mPtr != 0 because mQuitting was previously false.
? ? ? ? nativeWake(mPtr);
? ? }
}
quitSafely()會(huì)等待當(dāng)前消息執(zhí)行完畢后退出消息循環(huán),而quit()方法會(huì)直接退出消息循環(huán)。
private void removeAllMessagesLocked() {
? ? // 獲取當(dāng)前 MessageQueue 的頭部消息
? ? Message p = mMessages;
? ? while (p != null) {
? ? ? ? // 循環(huán)遍歷所有的 Message
? ? ? ? Message n = p.next;
? ? ? ? // 回收消息,并把消息放入消息池
? ? ? ? p.recycleUnchecked();
? ? ? ? p = n;
? ? }
? ? // 將頭部消息置為空
? ? mMessages = null;
}
private void removeAllFutureMessagesLocked() {
? ? // 獲取系統(tǒng)從開(kāi)機(jī)到現(xiàn)在到時(shí)間
? ? final long now = SystemClock.uptimeMillis();
? ? // 將當(dāng)前的頭部消息賦值給 p
? ? Message p = mMessages;
? ? if (p != null) {
? ? ? ? if (p.when > now) {
? ? ? ? ? ? // 如果當(dāng)前頭部消息將要執(zhí)行的時(shí)間大于系統(tǒng)開(kāi)機(jī)到現(xiàn)在的時(shí)間,則執(zhí)行 removeAllMessagesLocked() 方法
? ? ? ? ? ? // 清空 MessageQueue 隊(duì)列
? ? ? ? ? ? removeAllMessagesLocked();
? ? ? ? } else {
? ? ? ? ? ? Message n;
? ? ? ? ? ? // 遍歷當(dāng)前的 MessageQueue,直到某個(gè)消息的執(zhí)行時(shí)間小于 now 值(即這個(gè)消息正在執(zhí)行)
? ? ? ? ? ? // 將這個(gè)消息的 next 賦值為 null
? ? ? ? ? ? for (;;) {
? ? ? ? ? ? ? ? n = p.next;
? ? ? ? ? ? ? ? if (n == null) {
? ? ? ? ? ? ? ? ? ? return;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (n.when > now) {
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? p = n;
? ? ? ? ? ? }
? ? ? ? ? ? p.next = null;
? ? ? ? ? ? // 回收不會(huì)被執(zhí)行的 Message
? ? ? ? ? ? do {
? ? ? ? ? ? ? ? p = n;
? ? ? ? ? ? ? ? n = p.next;
? ? ? ? ? ? ? ? p.recycleUnchecked();
? ? ? ? ? ? } while (n != null);
? ? ? ? }
? ? }
}
終于講完了,希望大家能通過(guò)我的文章,徹底理解 Handler 的機(jī)制,但我的能力有限,如果存在錯(cuò)誤的地方,還請(qǐng)指出。
零碎的東西很多,為了方便大家記憶,我把上面的內(nèi)容做成了思維導(dǎo)圖,需要的朋友可以保存下來(lái),偶爾看一下,幫助自己記憶。
歡迎關(guān)注本文作者:可愛(ài)的肥臉