前言
Handler系列文章共兩篇:
關(guān)于Handler,想必大家都已經(jīng)非常熟悉了,它是Android中非常基礎(chǔ),但同時(shí)也極其重要的消息機(jī)制,說(shuō)它基礎(chǔ),是因?yàn)樗褂煤?jiǎn)單,在我們一開始學(xué)習(xí)Android時(shí),就會(huì)接觸到Handler,用它來(lái)進(jìn)行線程間的通信。說(shuō)它極其重要,是因?yàn)樗贏ndroid系統(tǒng)中扮演了一個(gè)極其核心的角色,可以說(shuō)只要有異步通信的地方就一定會(huì)有Handler,正是因?yàn)樗拇嬖冢沟梦覀傾ndroid系統(tǒng)中的很多組件能夠正常的運(yùn)行
注意:本文所展示的系統(tǒng)源碼都是基于Android-29 ,并提取核心部分進(jìn)行分析
問(wèn)題
下面我在這里拋出一些問(wèn)題,如果你都知道,那么恭喜你,你對(duì)Handler機(jī)制掌握的很透徹,如果你對(duì)下面這些問(wèn)題有一些疑惑,那么你就可以接著往下看,我會(huì)由淺入深的給你講解Handler機(jī)制,看完之后,這些問(wèn)題你就都會(huì)非常的明了,同時(shí)在最后我也會(huì)對(duì)這些問(wèn)題給出自己的回答
- Handler有哪些作用?
- 為什么我們能在主線程直接使用Handler,而不需要?jiǎng)?chuàng)建Looper?
- 如果想要在子線程創(chuàng)建Handler,需要做什么準(zhǔn)備?
- 一個(gè)線程有幾個(gè)Handler?
- 一個(gè)線程有幾個(gè)Looper?如何保證?
- 為什么Lopper死循環(huán),卻不會(huì)導(dǎo)致應(yīng)用卡死?
- Handler內(nèi)存泄露原因? 如何解決?
- 線程維護(hù)的Looper,在消息隊(duì)列無(wú)消息時(shí)的處理方案是什么?有什么用?
- 我們可以使用多個(gè)Handler往消息隊(duì)列中添加數(shù)據(jù),那么可能存在發(fā)消息的Handler存在不同的線程,那么Handler是如何保證MessageQueue并發(fā)訪問(wèn)安全的呢?
- Handler是如何進(jìn)行線程切換的呢?
- 我們?cè)谑褂肕essage的時(shí)候,應(yīng)該如何去創(chuàng)建它?
- Handler里面藏著的CallBack能做什么?
- Handler阻塞喚醒機(jī)制是怎么一回事?
- 什么是Handler的同步屏障?
- 能不能讓一個(gè)Message被加急處理?
什么是Handler?
我們通常所說(shuō)的Handler,他其實(shí)是Handler機(jī)制中的一個(gè)角色,只不過(guò)我們對(duì)Handler接觸比較多,因此用Handler來(lái)代稱
Handler機(jī)制是Android中基于單線消息隊(duì)列模式的一套線程消息機(jī)制。
Handler基本用法
//在主線程創(chuàng)建Handler實(shí)例
private Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
//處理接收的消息
}
};
//在適當(dāng)?shù)臅r(shí)機(jī)使用Handler實(shí)例發(fā)送消息
mHandler.sendMessage(message);
mHandler.post(runnable);//Runnable會(huì)被封裝進(jìn)一個(gè)Message,所以它本質(zhì)上還是一個(gè)Message
看上面這段代碼,創(chuàng)建了一個(gè)Handler實(shí)例并重寫了 handleMessage 方法 ,然后在適當(dāng)?shù)臅r(shí)機(jī)調(diào)用它的 send 或者 post 系列方法就可以了,使用就是這么簡(jiǎn)單
那么問(wèn)題來(lái)了,它們是如何進(jìn)行線程間的通信的呢? 下面我們就需要對(duì)源碼進(jìn)行分析
Handler機(jī)制源碼分析
在分析源碼之前,我先講下Handler機(jī)制涉及的幾大角色: Handler,Looper,MessageQueue,Message
先提前介紹下這幾個(gè)角色的作用,便于后續(xù)分析源碼的一個(gè)理解
Handler: 發(fā)送消息和處理消息
Looper: 從MessageQueue中獲取Message,然后交給Handler處理
MessageQueue: 消息隊(duì)列,存放Handler發(fā)送過(guò)來(lái)的消息
Message: 消息的載體
下面我們開始進(jìn)行源碼分析,在我們一開始使用的時(shí)候,創(chuàng)建了一個(gè)Handler實(shí)例,那我們看下它實(shí)例化的這個(gè)構(gòu)造方法:
public Handler() {
this(null, false);
}
它其實(shí)是調(diào)用了它的一個(gè)重載的方法,接著看它的重載方法
注意:
- Handler的構(gòu)造方法中還可以傳入Looper,通過(guò)傳入Looper的構(gòu)造方法可以實(shí)現(xiàn)一些特殊的功能
- Handler的構(gòu)造方法中還可以傳入Callback,這種方式創(chuàng)建一個(gè)Handler的實(shí)例,它并不需要派生出一個(gè)子類,后面我也會(huì)介紹到
- 有些構(gòu)造方法使用了
@UnsupportedAppUsage注解,表示不支持外部應(yīng)用調(diào)用
public Handler(@Nullable Callback callback, boolean async) {
//...
//獲取當(dāng)前線程的Lopper
mLooper = Looper.myLooper();
//如果當(dāng)前Looper為空,則拋出異常
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//將當(dāng)前Lopper中的MessageQueue賦值給Handler中的MessageQueue
mQueue = mLooper.mQueue;
//...
}
//---------------------以下為額外擴(kuò)展內(nèi)容--------------------------
//傳入Looper的構(gòu)造方法
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
//傳入Callback的構(gòu)造方法
public Handler(@Nullable Callback callback) {
this(callback, false);
}
//使用了@UnsupportedAppUsage注解的構(gòu)造方法
@UnsupportedAppUsage
public Handler(boolean async) {
this(null, async);
}
上面這段代碼注釋寫的很清楚,那我們是不是就可以得出一個(gè)結(jié)論: 我們?cè)趧?chuàng)建Handler實(shí)例的時(shí)候,一定要先創(chuàng)建一個(gè)Lopper,并開啟循環(huán)讀取消息,那么大家肯定有個(gè)疑問(wèn)? 你上面的使用就沒(méi)有創(chuàng)建Lopper,那是因?yàn)槲覀兊闹骶€程已經(jīng)給我們創(chuàng)建了一個(gè)Lopper
接下來(lái)我們找下主線程的這個(gè)Lopper是在哪里創(chuàng)建的,我們找到ActivityThread的main()方法
public static void main(String[] args) {
//...
//創(chuàng)建Lopper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
//...
//開啟循環(huán)讀取消息
Looper.loop();
//Looper如果因異常原因停止循環(huán)則拋異常
throw new RuntimeException("Main thread loop unexpectedly exited");
}
注意:通常我們認(rèn)為 ActivityThread 就是主線程。事實(shí)上它并不是一個(gè)線程,而是主線程操作的管理者,所以們把 ActivityThread 認(rèn)為就是主線程無(wú)可厚非,另外主線程也可以說(shuō)成 UI 線程。
我們?cè)?ActivityThread里的main方法里調(diào)用了Looper.prepareMainLooper() 方法創(chuàng)建了主線程的Looper ,并且調(diào)用了loop方法,所以我們就可以直接使用 Handler
繼續(xù)分析,我們知道m(xù)ain()方法是Java程序的入口,同時(shí)也是Android應(yīng)用程序的入口,而在Java中,我們執(zhí)行完main()方法,馬上就退出了,而在Android中,為啥沒(méi)有退出呢?這里我們做個(gè)假設(shè),如果在Android中也退出了,那么是不是Android就沒(méi)得玩了,所以Google肯定是不能讓他退出的,之所以在Android中沒(méi)有退出,正是因?yàn)槲覀冊(cè)谶@里創(chuàng)建并開啟了Looper死循環(huán),他會(huì)循環(huán)執(zhí)行各種事物。Looper死循環(huán)說(shuō)明線程沒(méi)有死亡,如果Looper停止循環(huán),線程則結(jié)束退出了
那么大家是不是又會(huì)有個(gè)疑問(wèn)?既然是一個(gè)死循環(huán),那為啥不會(huì)造成ANR?
其實(shí)Lopper死循環(huán)和程序ANR沒(méi)有任何關(guān)系,這里感覺(jué)就是在進(jìn)行一個(gè)概念的混淆,這里我解釋一下這兩個(gè)概念
ANR: 全稱Applicationn Not Responding,中文意思是應(yīng)用無(wú)響應(yīng),當(dāng)我發(fā)送一個(gè)消息到主線程,經(jīng)過(guò)一定時(shí)間沒(méi)有被執(zhí)行,那么這個(gè)時(shí)候就會(huì)拋出ANR異常
Lopper死循環(huán): 循環(huán)執(zhí)行各種事務(wù),當(dāng)Looper處理完所有消息的時(shí)候會(huì)進(jìn)入阻塞狀態(tài),當(dāng)有新的Message進(jìn)來(lái)的時(shí)候會(huì)打破阻塞繼續(xù)執(zhí)行
到了這里,相信大家對(duì)于創(chuàng)建Handler已經(jīng)很明了了,下面我們來(lái)實(shí)際應(yīng)用一下,在子線程創(chuàng)建Handler,直接上代碼:
public static class MyThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(@NonNull Message msg) {
//處理接收的消息
}
};
Looper.loop();
}
}
好,到了這里,我們應(yīng)該對(duì)創(chuàng)建Handler實(shí)例的時(shí)候,一定要先創(chuàng)建一個(gè)Lopper,并開啟循環(huán)讀取消息,有了深刻的理解,我們繼續(xù)分析源碼
上面說(shuō)了創(chuàng)建Handler實(shí)例之前要先創(chuàng)建Looper并開啟循環(huán),那我們分析下創(chuàng)建Lopper并開啟循環(huán)這個(gè)過(guò)程,先看下ActivityThread里的main方法里調(diào)用的Looper.prepareMainLooper()
public static void prepareMainLooper() {
//創(chuàng)建Looper,參數(shù)false表示該Looper不能退出
prepare(false);
//添加同步鎖
synchronized (Looper.class) {
//如果當(dāng)前sMainLooper已經(jīng)存在,則拋異常
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//將當(dāng)前線程的Looper實(shí)例賦值給sMainLooper
sMainLooper = myLooper();
}
}
實(shí)際上主要是調(diào)用了另外兩個(gè)方法,我們?cè)诳聪聀repare(false)和myLooper()方法的內(nèi)部實(shí)現(xiàn)
private static void prepare(boolean quitAllowed) {
//通過(guò)sThreadLocal獲取當(dāng)前Looper實(shí)例,如果當(dāng)前Lopper實(shí)例不為空則拋出異常
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//將new出來(lái)的Looper實(shí)例設(shè)置給sThreadLocal
sThreadLocal.set(new Looper(quitAllowed));
}
public static @Nullable Looper myLooper() {
//通過(guò)sThreadLocal獲取Looper實(shí)例對(duì)象
return sThreadLocal.get();
}
prepare方法: new一個(gè)Looper設(shè)置給sThreadLocal. myLooper方法: 通過(guò)sThreadLocal獲取Looper. 上面兩個(gè)方法,大家是不是會(huì)對(duì)這個(gè)sThreadLocal很好奇,這個(gè)東西有啥作用,我們根據(jù)上面兩個(gè)方法可以推斷出: sThreadLocal是用來(lái)存放Looper的
ThreadLocal介紹
ThreadLocal是Java中一個(gè)用于線程內(nèi)部存儲(chǔ)數(shù)據(jù)的工具類
看下面這一段代碼
public static void main(String[] args) {
ThreadLocal<Boolean> threadLocal = new ThreadLocal<>();
threadLocal.set(true);
Boolean aBoolean = threadLocal.get();
System.out.println("Current Thread " + Thread.currentThread().getName() + ": " + aBoolean);
//創(chuàng)建一個(gè)新的線程命名為a
new Thread("a"){
@Override
public void run() {
threadLocal.set(false);
Boolean bBoolean = threadLocal.get();
System.out.println("Current Thread " + Thread.currentThread().getName() + ": " + bBoolean);
}
}.start();
//創(chuàng)建一個(gè)新的線程命名為b
new Thread("b"){
@Override
public void run() { ;
Boolean cBoolean = threadLocal.get();
System.out.println("Current Thread " + Thread.currentThread().getName() + ": " + cBoolean);
}
}.start();
}
//打印結(jié)果:
Current Thread main: true
Current Thread a: false
Current Thread b: null
上面這段代碼:
- 在主線程創(chuàng)建了一個(gè)threadLocal變量,并調(diào)用
set方法設(shè)置為true,然后獲取該值并打印 - 創(chuàng)建一個(gè)新的線程,并調(diào)用
set方法設(shè)置值為false,獲取獲取該值并打印 - 創(chuàng)建一個(gè)新的線程,獲取該值并打印
從上面的日志可以看出,雖然在不同的線程中訪問(wèn)同一個(gè)threadLocal對(duì)象,但是它們通過(guò)ThreadLocal獲取的值卻是不一樣的,這就是ThreadLocal的奇妙之處,這里我又想問(wèn)一句,為什么? 凡事多問(wèn)幾個(gè)為什么,知識(shí)原理就學(xué)到手了,哈哈??,我們點(diǎn)進(jìn)去ThreadLocal的set方法看一下
public void set(T value) {
//獲取當(dāng)前線程
Thread t = Thread.currentThread();
//獲取當(dāng)前線程的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
//如果map不為空,則將當(dāng)前的ThreadLocal變量作為key,傳進(jìn)來(lái)的泛型作為value進(jìn)行存儲(chǔ)
map.set(this, value);
else
//如果map為空,則會(huì)創(chuàng)建map,將當(dāng)前的ThreadLocal變量作為key,傳進(jìn)來(lái)的泛型作為value進(jìn)行存儲(chǔ)
createMap(t, value);
}
通過(guò)上面代碼我們知道,通過(guò)獲取當(dāng)前線程的ThreadLocalMap,在把ThreadLocal變量作為key,傳進(jìn)來(lái)的泛型作為value進(jìn)行存儲(chǔ)
ThreadLocalMap它是ThreadLocal里面的一個(gè)靜態(tài)內(nèi)部類,它類似于一個(gè)改版的HashMap,內(nèi)部也是使用數(shù)組和Hash算法來(lái)存儲(chǔ)數(shù)據(jù),使得存儲(chǔ)和讀取的速度非???,因此這里我們使用HashMap的思想去理解ThreadLocalMap就好了,如果對(duì)ThreadLocalMap工作原理感興趣的,可以閱讀這篇文章傳送門
在看下get方法
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
獲取當(dāng)前線程的ThreadLocalMap,前面講到ThreadLocalMap其實(shí)非常像一個(gè)HashMap,他的get方法也是一樣的,使用ThreadLocal作為key獲取到對(duì)應(yīng)的Entry,再把value返回即可,如果map尚未初始化則會(huì)執(zhí)行初始化操作
因此我們是否可以得到結(jié)論:
ThreadLocal會(huì)從各自的線程,取出自己維護(hù)的ThreadLocalMap,其key為ThreadLocal,value為ThreadLocal對(duì)應(yīng)的泛型對(duì)象,這樣每個(gè)ThreadLocal就可以把自己作為key把不同的value存儲(chǔ)在不同的ThreadLocalMap,當(dāng)獲取數(shù)據(jù)的時(shí)候,同個(gè)ThreadLocal就可以從不同線程的ThreadLocalMap中得到不同的數(shù)據(jù)。因此當(dāng)我們以線程作為作用域,并且不同線程需要具有不同數(shù)據(jù)副本的時(shí)候,我們就可以考慮使用ThreadLocal。而Looper正好適用于這種場(chǎng)景
Looper介紹
上面我們分析到Looper使用ThreadLocal來(lái)保證每個(gè)線程有且只有一個(gè)相同的副本,因此我們可以得出結(jié)論: 一個(gè)線程對(duì)應(yīng)一個(gè)Looper,這個(gè)結(jié)論非常的重要,Handler機(jī)制之所以能夠?qū)崿F(xiàn)線程之間的通信,就是因?yàn)槭褂昧瞬煌€程的Looper處理消息,舉個(gè)例子: 我在線程A創(chuàng)建了幾個(gè)Hanlder實(shí)例處理消息,那我首先就要?jiǎng)?chuàng)建A線程的Looper并開啟消息循環(huán),那么我不管你這些Hanlder的實(shí)例從那個(gè)線程發(fā)送消息過(guò)來(lái),最終都會(huì)回到我A線程的MessageQueue中,然后通過(guò)A線程Looper不斷讀取消息,在交給當(dāng)前A線程的Handler來(lái)處理
Looper可以說(shuō)是Handler機(jī)制中的一個(gè)非常重要的核心。Looper相當(dāng)于線程消息機(jī)制的引擎,驅(qū)動(dòng)整個(gè)消息機(jī)制運(yùn)行。Looper負(fù)責(zé)從隊(duì)列中取出消息,然后交給對(duì)應(yīng)的Handler去處理。如果隊(duì)列中沒(méi)有消息,則MessageQueue的next方法會(huì)阻塞線程,等待新的消息的到來(lái)。每個(gè)線程有且只能有一個(gè)“引擎”,也就是Looper,如果沒(méi)有Looper,那么消息機(jī)制就運(yùn)行不起來(lái),而如果有多個(gè)Looper,則會(huì)違背單線操作的概念,造成并發(fā)操作。
Looper創(chuàng)建
在上面創(chuàng)建Looper的時(shí)候我們分析到:
主線程ActivityThread創(chuàng)建Looper,使用的是
prepareMainLooper方法,它是為主線程量身定做的,由于主線程的Looper比較特殊,所以Looper提供了一個(gè)getMainLooper方法,通過(guò)這個(gè)方法我們可以在任何地方獲取到主線程的Looper,且主線程的Looper不能退出我們自己創(chuàng)建的Looper,使用的是
prepare方法,實(shí)質(zhì)上它們最終都會(huì)調(diào)到prepare(boolean quitAllowed)這個(gè)方法,這個(gè)方法是私有的,外部不能直接調(diào)用,區(qū)別就是主線程創(chuàng)建的Looper不能退出,而我們自己創(chuàng)建的可以退出
//主線程
public static void prepareMainLooper() {
prepare(false);
//...
}
//獲取主線程Looper
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
//我們自己創(chuàng)建Looper
public static void prepare() {
prepare(true);
}
//參數(shù)quitAllowed true: 可退出 false: 不可退出
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
到這里我又有個(gè)疑問(wèn),為啥Looper不能直接在外部給New出來(lái)呢?我們點(diǎn)擊去Looper的構(gòu)造方法看一下:
private Looper(boolean quitAllowed) {
//創(chuàng)建一個(gè)MessageQueue,賦值給當(dāng)前Looper的mQueue
mQueue = new MessageQueue(quitAllowed);
//獲取當(dāng)前線程賦值給Looper的mThread
mThread = Thread.currentThread();
}
我們發(fā)現(xiàn),他的構(gòu)造方法是私有的,原來(lái)如此。而且我們還會(huì)發(fā)現(xiàn):Looper的內(nèi)部維護(hù)了一個(gè)MessageQueue,當(dāng)初始化Looper的時(shí)候會(huì)順帶初始化這個(gè)MessageQueue
Looper開啟消息循環(huán)
當(dāng)我們的Looper創(chuàng)建好后,他是不會(huì)自己?jiǎn)?dòng)的,需要我們手動(dòng)去啟動(dòng)Looper,調(diào)用Looper的loop()方法即可,所以前面創(chuàng)建Looper的時(shí)候我總是會(huì)說(shuō),創(chuàng)建Looper并開啟消息循環(huán),Looper的prepare和loop方法是配套使用的,兩者必須成對(duì)存在?,F(xiàn)在我們來(lái)重點(diǎn)分析一下Looper的loop方法,上源碼:
public static void loop() {
// 獲取當(dāng)前線程的Looper
final Looper me = myLooper();
//當(dāng)前線程的Looper,直接拋異常
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//獲取當(dāng)前Looper中的MessageQueue
final MessageQueue queue = me.mQueue;
//...
//開啟死循環(huán)讀取消息
for (;;) {
// 獲取消息隊(duì)列中的消息
Message msg = queue.next(); // might block
if (msg == null) {
// 返回null說(shuō)明MessageQueue退出了
return;
}
//...
try {
// 調(diào)用Message對(duì)應(yīng)的Handler處理消息
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
}
//...
// 回收Message
msg.recycleUnchecked();
}
}
loop方法就是Looper這個(gè)“引擎”的核心所在,他就像是一個(gè)開關(guān)
分析下這段代碼,首先獲取當(dāng)前線程的Looper對(duì)象,沒(méi)有則拋異常,然后進(jìn)入一個(gè)死循環(huán): 不斷調(diào)用MessageQueue的next方法來(lái)獲取消息,然后調(diào)用message的目標(biāo)handler的dispatchMessage方法來(lái)處理Message。
Looper退出
Looper提供了quit和quitSafely方法來(lái)退出一個(gè)Looper,二者的區(qū)別是: quit會(huì)直接退出Looper,而quitSafely只是設(shè)定一個(gè)標(biāo)記,然后把消息隊(duì)列中的已有消息處理完畢后才安全退出.在我們手動(dòng)創(chuàng)建Looper的情況下,如果所有的消息都被處理完成后,我們應(yīng)該調(diào)用quit方法來(lái)終止消息循環(huán),否則子線程就會(huì)一直處于等待狀態(tài),而如果退出Looper,這個(gè)線程就會(huì)立刻終止,因此建議不需要的時(shí)候終止Looper。
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
// 最終都是調(diào)用到了這個(gè)方法
void quit(boolean safe) {
// 如果不能退出則拋出異常。這個(gè)值在初始化Looper的時(shí)候被賦值
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
// 退出一次之后就無(wú)法再次運(yùn)行了
if (mQuitting) {
return;
}
mQuitting = true;
// 執(zhí)行不同的方法
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// 喚醒MessageQueue
nativeWake(mPtr);
}
}
quit和quitSafely方法最終都調(diào)用了quit(boolean safe)這個(gè)方法,這個(gè)方法先判斷是否能退出,然后再執(zhí)行退出邏輯。如果mQuitting==true,那么這里會(huì)直接return掉,我們會(huì)發(fā)現(xiàn)mQuitting這個(gè)變量只有在這里被執(zhí)行了賦值,所以一旦looper退出,則無(wú)法再次運(yùn)行了。之后執(zhí)行不同的退出邏輯,然后喚醒MessageQueue,之后MessageQueue的next方法會(huì)退出,Looper的loop方法也會(huì)跟著退出,那么線程也就停止了。
Looper總結(jié)
Looper作為Handler消息機(jī)制的“動(dòng)力引擎”,不斷從MessageQueue中獲取消息,然后交給Handler去處理。Looper的使用前需要先初始化當(dāng)前線程的Looper對(duì)象,再調(diào)用loop方法來(lái)啟動(dòng)它。
同時(shí)Handler也是實(shí)現(xiàn)切換的核心,因?yàn)椴煌腖ooper運(yùn)行在不同的線程,他所調(diào)用的dispatchMessage方法則運(yùn)行在不同的線程,所以Message的處理就被切換到Looper所在的線程了。當(dāng)looper不再使用時(shí),可調(diào)用不同的退出方法來(lái)退出他,注意Looper一旦退出,線程則會(huì)直接結(jié)束。
Handler發(fā)送消息
Handler和Looper都創(chuàng)建好了,那么接下來(lái)我們就要使用Handler去發(fā)送消息,我們?cè)谧铋_始介紹Handler使用的時(shí)候,寫了發(fā)送的兩種消息類型,如下:
//在適當(dāng)?shù)臅r(shí)機(jī)使用Handler實(shí)例發(fā)送消息
mHandler.sendMessage(message);
mHandler.post(runnable);//Runnable會(huì)被封裝進(jìn)一個(gè)Message,所以它本質(zhì)上還是一個(gè)Message
使用Handler發(fā)送消息,它有send 或者 post等一系列方法,最終這些發(fā)送的方法會(huì)調(diào)用到Handler中的enqueueMessage()方法,而Handler中的enqueueMessage方法最終會(huì)調(diào)用到MessageQueue的enqueueMessage方法,我們通過(guò)一個(gè)發(fā)送消息方法的源碼看下,以我們最常用的sendMessage()這個(gè)方法為例:
注意:post系列方法,發(fā)送的是一個(gè)Runnable,Runnable會(huì)被封裝進(jìn)一個(gè)Message,所以它本質(zhì)上還是一個(gè)Message
//1
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
//2
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//3
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
//4
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {
//將當(dāng)前的Handler賦值給Message的target屬性
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
以上代碼的調(diào)用順序就是1->2->3->4
這里我給一張圖來(lái)總結(jié)一下,send 或者 post等一系列方法的調(diào)用及最終的走向:
MessageQueue enqueueMessage方法介紹
到了這里,我們就來(lái)重點(diǎn)分析一下MessageQueue的enqueueMessage()方法,enqueueMessage中文意思是入隊(duì)消息,見(jiàn)名知意,這個(gè)方法就是把Handler發(fā)送的消息,放到消息隊(duì)列中
boolean enqueueMessage(Message msg, long when) {
// Hanlder為空則拋異常
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//當(dāng)前消息如果已經(jīng)已經(jīng)被執(zhí)行則拋異常
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
// 對(duì)MessageQueue進(jìn)行加鎖
synchronized (this) {
// 判斷目標(biāo)thread是否已經(jīng)死亡
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)記Message正在被執(zhí)行,以及需要被執(zhí)行的時(shí)間
//這里的when的值需要分情況:1,可能為0 2, 如果不為0,則是系統(tǒng)開機(jī)到現(xiàn)在的一個(gè)毫秒數(shù) + 延遲執(zhí)行的時(shí)間
//這兩種情況主要看你調(diào)用的是Handler哪個(gè)發(fā)送Message的方法
msg.markInUse();
msg.when = when;
// p是MessageQueue的鏈表頭
Message p = mMessages;
// 判斷是否需要喚醒MessageQueue
boolean needWake;
// 如果有新的隊(duì)頭,同時(shí)MessageQueue處于阻塞狀態(tài)則需要喚醒隊(duì)列
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//...
// 根據(jù)時(shí)間找到插入的位置
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
//...
}
msg.next = p;
prev.next = msg;
}
// 如果需要?jiǎng)t喚醒隊(duì)列
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
上述代碼我們來(lái)總結(jié)一下:
首先判斷Message中的Handler不能不空,且不能為在使用中,否則拋異常
對(duì)MessageQueue進(jìn)行加鎖,判斷當(dāng)前線程是否dead,如果dead則打印一個(gè)異常,并返回false
初始化Message的執(zhí)行時(shí)間以并且標(biāo)記為正在執(zhí)行中
當(dāng)新插入的Message在鏈表頭時(shí),如果messageQueue是空的或者正在等待下個(gè)延遲消息,則需要喚醒MessageQueue
-
根據(jù)Message的執(zhí)行時(shí)間,找到在鏈表中的插入位置進(jìn)行插入,這里我們可以理解MessageQueue中維護(hù)了一個(gè)優(yōu)先級(jí)隊(duì)列,
優(yōu)先級(jí)隊(duì)列就是鏈表根據(jù)時(shí)間進(jìn)行排序并加入隊(duì)列的數(shù)據(jù)結(jié)構(gòu)形成的,例如我們發(fā)送的幾個(gè)消息攜帶的時(shí)間分別為:1s,20ms,3s,那么這個(gè)時(shí)候就會(huì)根據(jù)時(shí)間進(jìn)行排序?yàn)椋?0ms,1s,3s, 那么如果我新加入的一個(gè)消息的時(shí)間為2s,那么他就會(huì)插入1s和3s的中間,此時(shí)這個(gè)優(yōu)先級(jí)隊(duì)列就有了4個(gè)元素: 20ms,1s,2s,3s
MessageQueue next方法介紹
到這里,Handler發(fā)送的消息已經(jīng)放到了MessageQueue中,那接著肯定就要進(jìn)行消息的讀取,我們剛講到Looper的Loop方法會(huì)從MessageQueue中循環(huán)讀取消息,loop方法中調(diào)用queue.next()的地方有句源碼注釋:might block,中文意思是可能被阻塞,如下:
public static void loop() {
//獲取當(dāng)前Looper中的MessageQueue
final MessageQueue queue = me.mQueue;
//...
//開啟死循環(huán)讀取消息
for (;;) {
// 獲取消息隊(duì)列中的消息
Message msg = queue.next(); // might block
}
//...
}
我們就看下MessageQueue的next方法到底做了什么:
Message next() {
// Return here if the message loop has already quit and been disposed.
// 源碼中的注釋表示:如果looper已經(jīng)退出了,這里就返回null
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
//...
// 定義阻塞時(shí)間賦值為0
int nextPollTimeoutMillis = 0;
//死循環(huán)
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 阻塞對(duì)應(yīng)時(shí)間 這個(gè)方法最終會(huì)調(diào)用到linux的epoll機(jī)制
nativePollOnce(ptr, nextPollTimeoutMillis);
// 對(duì)MessageQueue進(jìn)行加鎖,保證線程安全
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//...
if (msg != null) {
if (now < msg.when) {
// 下一個(gè)消息還沒(méi)開始,等待兩者的時(shí)間差
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 獲得消息且現(xiàn)在要執(zhí)行,標(biāo)記MessageQueue為非阻塞
mBlocked = false;
// 鏈表操作
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
// 沒(méi)有消息,進(jìn)入阻塞狀態(tài)
nextPollTimeoutMillis = -1;
}
//退出
if (mQuitting) {
dispose();
return null;
}
//...涉及了同步屏障和IdleHandler,后續(xù)在分析
}
}
從上面代碼我們發(fā)現(xiàn)next方法目的是獲取MessageQueue中的一個(gè)Message,它里面有一個(gè)死循環(huán),如果消息隊(duì)列中沒(méi)有消息,那么next方法會(huì)一直阻塞在這里,當(dāng)有新消息到來(lái)時(shí),就會(huì)將它喚醒,next方法會(huì)返回這條消息并將其從優(yōu)先級(jí)隊(duì)列中給移除
步驟如下:
- 如果Looper已經(jīng)退出了,直接返回null
- 進(jìn)入死循環(huán),直到獲取到Message或者退出
- 循環(huán)中先判斷是否需要進(jìn)行阻塞,阻塞最終會(huì)調(diào)用到linux的epoll機(jī)制,阻塞結(jié)束后,對(duì)MessageQueue進(jìn)行加鎖,獲取Message
- 如果MessageQueue中沒(méi)有消息,則直接把線程無(wú)限阻塞等待喚醒
- 如果MessageQueue中有消息,則判斷是否需要等待,否則則直接返回對(duì)應(yīng)的message
可以看到邏輯就是判斷當(dāng)前時(shí)間Message中是否需要等待.其中nextPollTimeoutMillis表示阻塞的時(shí)間,-1表示無(wú)限時(shí)間,直到有事件發(fā)生為止,0表示不阻塞
Handler接收消息
在我們對(duì)Looper進(jìn)行總結(jié)時(shí)我們說(shuō)了: Handler也是實(shí)現(xiàn)線程切換的核心,因?yàn)椴煌腖ooper運(yùn)行在不同的線程,他所調(diào)用的dispatchMessage方法則會(huì)運(yùn)行在不同的線程,所以Message的處理就會(huì)被切換到Looper所在的線程
public static void loop() {
for (;;) {
//...
try {
// 調(diào)用Message對(duì)應(yīng)的Handler處理消息
msg.target.dispatchMessage(msg);
}
//...
}
}
上面代碼調(diào)用了 msg.target.dispatchMessage(msg) 方法,msg.target 就是發(fā)送該消息的 Handler,這樣消息最終會(huì)回調(diào)到Handler的dispatchMessage方法中,看下這個(gè)方法
public void dispatchMessage(@NonNull Message msg) {
//消息的callback不為空,則回調(diào)handleCallback方法
if (msg.callback != null) {
handleCallback(msg);
} else {
//當(dāng)前mCallback不為空,回調(diào)mCallback.handleMessage方法
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//回調(diào)handleMessage
handleMessage(msg);
}
}
上述代碼步驟:
1、首先,檢查Message的callback是否為null,不為null就通過(guò)handleCallBack來(lái)處理消息,Message的callback是一個(gè)Runnable對(duì)象,實(shí)際上就是Handler的post系列方法所傳遞的Runnable參數(shù),handleCallBack方法處理邏輯也很簡(jiǎn)單,如下:
private static void handleCallback(Message message) {
message.callback.run();
}
2、其次,檢查mCallback是否為null,不為null就調(diào)用mCallback的handleMessage方法來(lái)處理消息。Callback是個(gè)接口,如下:
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*/
public interface Callback {
boolean handleMessage(@NonNull Message msg);
}
通過(guò)Callback可以采用如下方式來(lái)創(chuàng)建Handlere對(duì)象:
Handler handler = new Handler(callback);
那Callback的意義是什么呢?源碼里注釋做了說(shuō)明:可以用來(lái)創(chuàng)建一個(gè)Handler的實(shí)例但并不需要派生的子類。在日常開發(fā)中,創(chuàng)建Handler最常見(jiàn)的就是派生一個(gè)Handler的子類并重寫其handleMessage方法來(lái)處理具體的消息,而Callback給我們提供了另外一種使用Handler的方式,當(dāng)我們不想派生子類時(shí),就可以通過(guò)Callback來(lái)實(shí)現(xiàn)。
3、最后,調(diào)用Handler的handleMessage方法來(lái)處理消息
Handler處理消息的過(guò)程我畫了一張圖,如下:
Message介紹
Message是負(fù)責(zé)承載消息的類,主要是關(guān)注他的內(nèi)部屬性:
// 用戶自定義,主要用于辨別Message的類型
public int what;
// 用于存儲(chǔ)一些整型數(shù)據(jù)
public int arg1;
public int arg2;
// 可放入一個(gè)可序列化對(duì)象
public Object obj;
// Bundle數(shù)據(jù)
Bundle data;
// Message處理的時(shí)間。相對(duì)于1970.1.1而言的時(shí)間
// 對(duì)用戶不可見(jiàn)
public long when;
// 處理這個(gè)Message的Handler
// 對(duì)用戶不可見(jiàn)
Handler target;
// 當(dāng)我們使用Handler的post方法時(shí)候就是把runnable對(duì)象封裝成Message
// 對(duì)用戶不可見(jiàn)
Runnable callback;
// MessageQueue是一個(gè)鏈表,next表示下一個(gè)
// 對(duì)用戶不可見(jiàn)
Message next;
循環(huán)利用Message
當(dāng)我們獲取Message的時(shí)候,官方建議是通過(guò)Message.obtain()方法來(lái)獲取,當(dāng)使用完之后使用recycle()方法來(lái)回收循環(huán)利用。而不是直接new一個(gè)新的對(duì)象:
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0;
sPoolSize--;
return m;
}
}
return new Message();
}
Message維護(hù)了一個(gè)靜態(tài)鏈表,鏈表頭是sPool,Message有一個(gè)next屬性,Message本身就是鏈表結(jié)構(gòu)。sPoolSync是一個(gè)object對(duì)象,僅作為解決并發(fā)訪問(wèn)安全設(shè)計(jì)。當(dāng)我們調(diào)用obtain來(lái)獲取一個(gè)新的Message的時(shí)候,首先會(huì)檢查鏈表中是否有空閑的Message,如果沒(méi)有則新建一個(gè)返回。
當(dāng)我們使用完成之后,可以調(diào)用Message的recycle方法進(jìn)行回收,如果這個(gè)Message正在使用則會(huì)拋出異常,否則則調(diào)用recycleUnchecked進(jìn)行回收,把Message中的內(nèi)容清空,然后判斷鏈表是否達(dá)到最大值(50),然后插入鏈表中
Handler消息機(jī)制原理總結(jié)
通過(guò)上面的源碼分析,我們可以得出結(jié)論:
- 實(shí)例化Handler之前,需先構(gòu)建當(dāng)前線程的Looper并開啟消息循環(huán)
- 通過(guò)Handler的send和post方法發(fā)送消息
- 發(fā)送的消息會(huì)加入到MessageQueue中,等待Looper獲取處理
- Looper會(huì)不斷地從MessageQueue中獲取Message然后交付給對(duì)應(yīng)的Handler處理
如果到這里你還不是特別清楚Handler消息機(jī)制的原理,那么繼續(xù)看下面這張圖:
好了,到了這里,關(guān)于Handler消息機(jī)制的主體部分就講完了。
限于篇幅,本篇文章就到這里了,后續(xù)我會(huì)在寫一篇關(guān)于Handler的文章,介紹Hanlder的一些擴(kuò)展知識(shí)學(xué)習(xí),并回答前面我所列出來(lái)的一系列問(wèn)題
全文到此,原創(chuàng)不易,歡迎點(diǎn)贊,收藏,評(píng)論和轉(zhuǎn)發(fā),你的認(rèn)可是我創(chuàng)作的動(dòng)力