1.前言
關(guān)于Handler消息機制的博客實際上是非常多的了。
之前也是看別人的博客過來的,但是過了一段時間之后,一些細節(jié)也就忘了。
所以,就自己擼一篇,權(quán)當筆記,方便以后翻閱。
這篇文章主要是分析Handler消息機制原理以及收集一些面試題來講解,熟悉的話可以不用看了。
本文源碼基于android 27。
2.Android消息機制概述
2.1 本質(zhì)
Android的消息機制本質(zhì)就是一套消息發(fā)送,傳遞及處理的機制。
2.2 角色說明
| 角色 | 作用 |
|---|---|
Handler(處理者) |
負責(zé)消息的發(fā)送及處理等等。 |
Message(消息) |
保存消息的內(nèi)容,如保存一個消息類型(what)等等。 |
MessageQueue(消息隊列) |
保存Message的數(shù)據(jù)結(jié)構(gòu),是一個鏈表。 |
Looper(循環(huán)器) |
從消息隊列中循環(huán)的取出消息然后把消息交給Handler處理。 |
2.3 原理簡介
消息機制在Android上的流程為:
- 應(yīng)用啟動時會在主線程中創(chuàng)建一個
Looper(循環(huán)器),Looper(循環(huán)器)內(nèi)部則會創(chuàng)建一個MessageQueue(消息隊列);
- 然后
Looper(循環(huán)器)就會不斷的輪詢MessageQueue(消息隊列)中是否有Message(消息); - 我們可以通過
Handler去發(fā)送一個Message(消息),發(fā)送之后Message(消息)就會進入到MessageQueue(消息隊列)中去,Looper(循環(huán)器)通過輪詢?nèi)〕?code>Message(消息),然后交給相應(yīng)的Handler(處理者)去處理。
下面會通過分析源碼來驗證這個過程。
上面這個流程總結(jié)成一句話就是:
Looper(循環(huán)器)通過不斷的從MessageQueue(消息隊列)中取出Message(消息),然后交給相應(yīng)的Handler(處理者)去處理。
是不是很簡單呢?
2.4 Handler的應(yīng)用--UI更新
對于消息機制,我們平常接觸的最多的場景就是:
在子線程中進行一些數(shù)據(jù)更新等耗時操作,然后使用
Handler在主線程中去更新UI。
為什么要怎么操作呢?這里有兩個前提:
1.Android開發(fā)中規(guī)定了UI的更新只能在主線程中去操作,在子線程中更新會報錯。
2.我們在主線程中創(chuàng)建的Handler能夠接受到同一個Handler在子線程中發(fā)送的消息。
可以看到,在這種場景下我們使用Handler的目的就是切換到主線程中去更新UI。而Handler的使用方式是很簡單的,這里就不寫例子了。
那么,為什么更新UI只能在主線程中去操作呢?
這是因為Android中的UI控件不是線程安全的,因此在多線程中并發(fā),可能會出現(xiàn)線程安全的問題,即訪問UI控件可能會出現(xiàn)跟預(yù)期不一樣的結(jié)果。那么為什么不使用鎖機制呢?因為加鎖會降低訪問UI的效率,鎖機制會阻塞某些線程的執(zhí)行。因此,最簡單高效的方法就是使用單線程模型來進行UI的訪問了。
那么,為什么主線程中的Handler能接受到其他線程發(fā)來的消息呢?
這是后面源碼分析的內(nèi)容,這里暫且不表。
2.5 Handler的其他應(yīng)用
上面UI更新實際上只是消息機制其中一個應(yīng)用場景。
如果我們了解四大組件的啟動停止等過程的話,就會發(fā)現(xiàn),都是在一個名為H的Handler中處理狀態(tài)切換等邏輯,這個H是ActivityThread的內(nèi)部類。其本質(zhì)就是切到主線程中去處理。
所以說,不要將Handler僅僅局限于UI更新。
3.源碼分析
本節(jié)主要深入源碼對消息機制進行分析。對涉及到Looper、MessageQueue、Message、Handler等類進行逐一分析。
3.1 Looper類
3.1.1 Looper(循環(huán)器)的創(chuàng)建
Looper的創(chuàng)建可以分為在主線程中創(chuàng)建以及在子線程中創(chuàng)建,我們分別來看下。
3.1.1.1 主線程中創(chuàng)建Looper
先來看下Looper在主線程中是什么時候創(chuàng)建的。
3.1.1.1.1 ActivityThread的main()
應(yīng)用啟動時,會調(diào)用到ActivityThread中的main()方法,這個main()方法是應(yīng)用程序的入口。main()里面會創(chuàng)建一個Looper對象出來。我們來看下代碼:
public static void main(String[] args) {
//省略無關(guān)代碼
//為主線程創(chuàng)建1個Looper對象
Looper.prepareMainLooper();
//創(chuàng)建主線程
ActivityThread thread = new ActivityThread();
thread.attach(false);
//省略無關(guān)代碼
//開啟消息循環(huán)
Looper.loop();
}
可以看到,應(yīng)用啟動時就為主線程創(chuàng)建出一個Looper對象,并且開啟消息循環(huán)。
3.1.1.1.2 Looper的prepareMainLooper()
再來看下prepareMainLooper():
public static void prepareMainLooper() {
//最終還是調(diào)用prepare()
//參數(shù)false表示主線程中的消息循環(huán)不允許退出
prepare(false);
//判斷sMainLooper是否為null,否則拋出異常
//即prepareMainLooper()不能夠被調(diào)用兩次
//保證了主線程中只存在一個Looper對象
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
3.1.1.1.3 Looper的prepare()
再來看下prepare()方法:
private static void prepare(boolean quitAllowed) {
//ThreadLocal可以保存一個線程內(nèi)的局部變量
//這里判斷當前線程是否已經(jīng)存在Looper對象,存在的話則拋異常
//因為一個線程只能創(chuàng)建一個Looper對象
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//當前線程沒有創(chuàng)建Looper對象的話
//則新創(chuàng)建一個Looper對象
//并把這個Looper對象保存到ThreadLocal中
sThreadLocal.set(new Looper(quitAllowed));
}
prepare()中就是創(chuàng)建一個Looper對象并把Looper對象保存到線程中的ThreadLocal。
3.1.1.1.4 Looper的構(gòu)造方法
再來看下Looper的構(gòu)造方法:
private Looper(boolean quitAllowed) {
//創(chuàng)建消息隊列
mQueue = new MessageQueue(quitAllowed);
//記錄當前線程.
mThread = Thread.currentThread();
}
Looper內(nèi)部中就是創(chuàng)建了一個消息隊列。
3.1.1.1.5 小結(jié)
應(yīng)用啟動時,主線程會創(chuàng)建一個Looper對象出來,Looper內(nèi)部則創(chuàng)建消息隊列。
3.1.1.2 子線程中創(chuàng)建Looper
在子線程中創(chuàng)建Looper非常簡單,直接看例子吧:
3.1.1.2.1 子線程中創(chuàng)建Looper例子
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
//TODO ...
}
};
Looper.loop();
}
調(diào)用一下無參數(shù)的prepare()方法即可。
3.1.1.2.2 Looper的prepare()
public static void prepare() {
prepare(true);
}
最終還是調(diào)用有參數(shù)的prepare()方法,有參數(shù)的prepare()方法祥見上面的代碼分析。
true代表這個Looper是可以退出的。主線程中創(chuàng)建的Looper則是不能退出的。這就是他們的區(qū)別。
3.1.2 Looper(循環(huán)器)的消息循環(huán)
Looper是通過loop()這個方法來進行消息循環(huán)的,我們來看下代碼:
3.1.2.1 Looper的loop()
public static void loop() {
//獲得當前線程的Looper對象
//myLooper()實際上通過sThreadLocal.get()來獲取的
final Looper me = myLooper();
//如果當前線程沒有創(chuàng)建過Looper,則拋出異常
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//獲得Looper對象中的消息隊列
final MessageQueue queue = me.mQueue;
//死循環(huán)
for (;;) {
//從消息隊列中取出一個消息
//如果消息隊列沒有消息的話,會阻塞在這里
Message msg = queue.next(); // might block
//消息為null的話表示停止消息循環(huán)
//可以通過queue.quit()來停止,前提是通過prepare(true);來創(chuàng)建的
//主線程中不允許停止消息循環(huán)
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//...
//分發(fā)消息去處理
//msg.target就是要處理的Handler,祥見后面分析
msg.target.dispatchMessage(msg);
//...
//回收消息
msg.recycleUnchecked();
}
}
3.1.2.2 小結(jié)
Looper中通過一個死循環(huán)從消息隊列中取消息,一旦取到消息之后,就分發(fā)交給Handler來處理。
3.1.3 Looper(循環(huán)器)的退出
public void quit() {
mQueue.quit(false);
}
//安全退出
public void quitSafely() {
mQueue.quit(true);
}
退出Looper有兩個方法,如上。最終還是通過調(diào)用MessageQueue.quit(boolean safe)方法來實現(xiàn),只是傳的參數(shù)不一樣而已。這個在MessageQueue那一小節(jié)再來分析。
3.2 Message類
Message類用來保存消息的內(nèi)容。我們先來看下Message類會保存哪些消息:
3.2.1 Message類主要成員變量
| 成員變量 | 類型 | 含義 |
|---|---|---|
| what | int | 消息類型 |
| obj | Object | 消息內(nèi)容 |
| when | long | 消息觸發(fā)時間 |
| target | Handler | 消息處理者 |
| callback | Runnable | 回調(diào)方法 |
| sPool | Message | 消息池 |
| sPoolSize | int | 消息池大小 |
| next | Message | 下一條消息 |
這里只列舉了一部分,詳細的可以去看Message類的源碼。
3.2.2 獲取消息
Message內(nèi)部維護了一個消息池,我們可以通過obtain()來從消息池中獲取消息,而不是直接去new,這樣可以提高效率。
3.2.2.1 Message的obtain()
public static Message obtain() {
synchronized (sPoolSync) {
//如果消息池不為null,則從消息池中取出一條消息
if (sPool != null) {
//從sPool中取出頭結(jié)點
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // 清除in-use標記
sPoolSize--;
return m;
}
}
//如果消息池為null,則直接new
return new Message();
}
3.2.3 回收消息
3.2.3.1 Message的recycle()
public void recycle() {
//判斷消息是否正在使用
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
//調(diào)用recycleUnchecked()
recycleUnchecked();
}
調(diào)用recycleUnchecked()來回收。
3.2.3.2 Message的recycleUnchecked()
void recycleUnchecked() {
//將消息標記為使用狀態(tài)
//清空消息其他參數(shù)
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
//如果沒有達到消息池的最大容量,則將消息回收到消息池中去
//最大容量默認為50
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
3.2.4 小結(jié)
Message內(nèi)部維護了一個消息池,這個消息池是以鏈表的形式存在的。通過消息池去獲取Message對象能夠避免直接創(chuàng)建對象,可以起到一個提高效率的作用。
3.3 Handler類
通常,我們都會通過繼承Handler類來自定義一個Handler子類,然后重寫handleMessage()方法來處理消息。同時,也是通過這個Handler子類來進行發(fā)送消息的。
我們先來看下Handler的構(gòu)造方法。
3.3.1 Handler的構(gòu)造方法
3.3.1.1 Handler的無參構(gòu)造方法
public Handler() {
this(null, false);
}
最終會調(diào)用有參數(shù)的構(gòu)造方法Handler(Callback callback, boolean async)
3.3.1.2 Handler的有參構(gòu)造方法
先來看下上面提到的Handler(Callback callback, boolean async):
public Handler(Callback callback, boolean async) {
//匿名類、內(nèi)部類或本地類應(yīng)該申明為static,否則會警告可能出現(xiàn)內(nèi)存泄露
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//獲得當前線程的Looper對象
//myLooper()實際上通過sThreadLocal.get()來獲取的
mLooper = Looper.myLooper();
//如果mLooper為null則拋出異常
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//獲得Looper對象中的消息隊列
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
下面列出所有Handler的所有有參構(gòu)造方法
public Handler(Callback callback);
public Handler(Looper looper);
public Handler(Looper looper, Callback callback);
public Handler(boolean async);
public Handler(Callback callback, boolean async);
public Handler(Looper looper, Callback callback, boolean async);
可以看到:除了callback和async之外,還可以傳遞一個Looper進來,即可以指定跟Handler要綁定的Looper,相關(guān)代碼就不貼了,還是很簡單的。
3.3.2 Handler發(fā)送消息
通常我們都是通過Handler的sendMessage()方法來發(fā)送消息的:
3.3.2.1 Handler的sendMessage()
public final boolean sendMessage(Message msg)
{
//調(diào)用sendMessageDelayed()
return sendMessageDelayed(msg, 0);
}
3.3.2.2 Handler的sendMessageDelayed()
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
//調(diào)用sendMessageDelayed()
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
3.3.2.3 Handler的sendMessageAtTime()
public boolean sendMessageAtTime(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;
}
//調(diào)用enqueueMessage(),消息入隊
return enqueueMessage(queue, msg, uptimeMillis);
}
3.3.2.4 Handler的enqueueMessage()
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//把當前的Handler對象保存到消息中的target中去
//這樣消息分發(fā)時才能找到相應(yīng)的Handler去處理
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//把消息放到消息隊列中去
return queue.enqueueMessage(msg, uptimeMillis);
}
最終,發(fā)送消息就是將消息放到消息隊列中去。
3.3.3 Handler發(fā)送Runnable
Handler除了sendMessage(Message msg)外,還可以發(fā)送一個Runnable出來,這是通過其post()方法實現(xiàn)的:
3.3.3.1 Handler的post()
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
post()方法里面同樣也是通過sendMessageDelayed()來發(fā)送消息,我們來看下getPostMessage(r)這個方法:
3.3.3.1 Handler的getPostMessage()
private static Message getPostMessage(Runnable r) {
//獲取消息
Message m = Message.obtain();
//Runnable賦值給Message中的callback
m.callback = r;
//返回一個Message
return m;
}
所以,post()最終還是將Runnable對象包裝成一個Message來進行消息發(fā)送的。
3.3.4 分發(fā)消息
前面Looper在消息隊列中取到消息后就調(diào)用msg.target.dispatchMessage(msg);來分發(fā)消息,這里的msg.target就是Handler。我們來看下dispatchMessage()方法:
3.3.4.1 Handler的dispatchMessagee()
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//當msg.callback不為null時,會回調(diào)message.callback.run()方法
//即執(zhí)行Runnable的run()方法
handleCallback(msg);
} else {
//當Handler存在Callback時,回調(diào)Callback的handleMessage();
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//調(diào)用Handler的handleMessage(msg)
//即我們繼承Handler重寫的handleMessage(msg)方法
handleMessage(msg);
}
}
我們再來看下handleCallback()這個方法:
3.3.4.2 Handler的handleCallback()
private static void handleCallback(Message message) {
//執(zhí)行Runnable的run()方法
message.callback.run();
}
3.3.4.3 小結(jié)
從上面的代碼可以看到,分發(fā)消息的流程如下:
- 如果
msg.callback不為null,這個msg.callback實際是個Runnable對象,則調(diào)用這個Runnable的run()方法,結(jié)束;為null的話就走到步驟2。- 如果
Handler的成員變量mCallback不為null,則調(diào)用mCallback.handleMessage(msg),結(jié)束;為null的話就走到步驟3。- 調(diào)用
Handler的handleMessage(msg)方法,結(jié)束。
3.4 MessageQueue類
我們再來看下MessageQueue類,主要包括消息入隊、取出消息和退出等。
3.4.1 MessageQueue構(gòu)造方法
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
//通過native方法去初始化化消息隊列
mPtr = nativeInit();
}
3.4.2 消息入隊
3.4.2.1 MessageQueue的enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
//判斷消息是否關(guān)聯(lián)了Handler,若無則拋異常
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//判斷消息是否用過,若用過則拋異常
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
//當前消息隊列是否正在退出
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();
//返回失敗結(jié)果
return false;
}
//標記消息為使用狀態(tài)
msg.markInUse();
//獲取message的when時間(觸發(fā)時間)
msg.when = when;
//獲取消息隊列里的消息
//Message是個鏈表結(jié)構(gòu)
Message p = mMessages;
boolean needWake;
//如果消息隊列里沒有消息
//或者發(fā)生時間(when)在鏈表的頭結(jié)點之前
if (p == null || when == 0 || when < p.when) {
//將消息插入到鏈表的頭結(jié)點,即放入隊頭
msg.next = p;
mMessages = msg;
//如果處于阻塞狀態(tài),則喚醒
needWake = mBlocked;
} else {
//如果消息隊列中已存在消息且觸發(fā)時間(when)在鏈表的頭結(jié)點之后
//則插入到隊列中間
//通常這里的needWake為false,即不需喚醒消息隊列
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//死循環(huán),找到當前消息比鏈表中的消息早發(fā)生的消息,插入到那條消息前面,否則就插入到鏈表表尾
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
//插入到隊列中間或隊尾
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) {//如果需要喚醒
//在native層喚醒消息隊列
nativeWake(mPtr);
}
}
return true;
}
可以看到,消息隊列是根據(jù)消息觸發(fā)時間來進行排隊的,觸發(fā)時間最早的消息將會排到隊列的頭部。當有新消息需要加入消息隊列時,會從隊列頭開始遍歷,直到找到消息應(yīng)該插入的合適位置,以保證所有消息的時間順序。
如果消息隊列需要喚醒,則會在消息加入消息隊列后對消息隊列進行喚醒。
3.4.3 取出消息
3.4.3.1 MessageQueue的next()
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
//當消息循環(huán)已經(jīng)退出,直接返回
return null;
}
//只有首次迭代為-1
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
//死循環(huán)
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//這里會阻塞,直到nextPollTimeoutMillis超時或者消息隊列被喚醒
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
//當target為空時,在消息隊列中循環(huán)查找到下一條異步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
//當消息觸發(fā)時間大于當前時間
if (now < msg.when) {
//下一條消息還沒準備好,設(shè)置一個超時喚醒
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 獲取一條消息
mBlocked = false;
//消息隊列中移除這條消息
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
//消息標記為使用狀態(tài)
msg.markInUse();
//返回消息
return msg;
}
} else {
//沒有消息
nextPollTimeoutMillis = -1;
}
//如果消息正在退出,返回null
if (mQuitting) {
dispose();
return null;
}
//如果當前是第一次循環(huán)時
//且當前消息隊列為空時,或者下一條消息還沒準備好時
//即當前處于空閑的狀態(tài)
//那么就獲取Idle Handler的數(shù)量
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
//沒有idle handlers 需要運行,繼續(xù)循環(huán)并等待。
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
//運行idle handlers
//只有第一次循環(huán)時才會執(zhí)行這些代碼塊
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
//釋放handler的引用
mPendingIdleHandlers[i] = null;
boolean keep = false;
try {
//執(zhí)行idler的queueIdle()
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
//根據(jù)idler.queueIdle()的返回值來判斷是否移除idler
//即返回true的話能夠重復(fù)執(zhí)行
mIdleHandlers.remove(idler);
}
}
}
//重置IdleHandler的數(shù)量為0,這樣就保證不會重復(fù)運行
//即只有第一次循環(huán)時會運行
pendingIdleHandlerCount = 0;
//重置超時時間為0
//即當調(diào)用一個idle handler時, 一個新的消息能夠被分發(fā),因此無需等待即可回去繼續(xù)查找還未被處理的消息
nextPollTimeoutMillis = 0;
}
}
取出消息時,如果沒有消息或者超時時間還沒到,則會處于阻塞的狀態(tài),直到超時時間過去或者消息隊列被喚醒。當消息準備好時,才會返回消息出去。
另外,如果當前處于空閑的狀態(tài),則會執(zhí)行IdleHandler中的方法。
3.4.4 消息隊列退出
3.4.4.1 MessageQueue的quit()
void quit(boolean safe) {
//主線程的消息隊列是不允許退出的,主要還是看mQuitAllowed的值
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
//如果正在退出,則直接返回,防止多次操作
if (mQuitting) {
return;
}
//設(shè)置為正在退出中
mQuitting = true;
//判斷是否安全移除
if (safe) {
//移除尚未觸發(fā)的所有消息
removeAllFutureMessagesLocked();
} else {
//移除所有的消息
removeAllMessagesLocked();
}
nativeWake(mPtr);
}
}
如果是安全退出,那么只會移除尚未觸發(fā)的所有消息,對于正在觸發(fā)的消息并不移除;
如果不是安全退出,則直接移除所有的消息。
4.一些面試題
這里收集了一些常見的面試題來解答一下,大部分答案其實都能在上面的分析中找到的,所以嘛,要認真看代碼。
這里只列出一部分題目,如果有好的題目也可以留言補充哈~
4.1 Q:Looper.loop()是個死循環(huán),主線程為什么不會卡死?
對于一個線程,如果執(zhí)行完代碼之后就會正常退出。但是對于主線程,我們肯定是希望能夠一直存活的,那么最簡單的方法就是寫個死循環(huán)讓它一直在執(zhí)行,這樣就不會退出了。那么主線程為什么不會卡死呢?如果消息隊列里面有消息,Looper就取出來出來;如果沒有消息就會阻塞,直到有新消息進來喚醒消息隊列去處理,這一過程就是在這個死循環(huán)中處理的,所以說Looper本身是不會卡死的。像Activity的onCreate()、onResume()等生命周期實際上就是在這個死循環(huán)中執(zhí)行的。如果我們在Activity的onCreate()、onResume()中做一些耗時操作,可能就會發(fā)生掉幀,甚至出現(xiàn)ANR,這才是我們看到的卡頓卡死現(xiàn)象。
4.2 Q:一個線程中是否可以有多個Handler?如果有多個,分發(fā)消息是如何區(qū)分的?
是可以有多個的。我們使用Handler發(fā)送Message時,Message中的target變量會保存當前的Handler引用,分發(fā)消息時就是靠這個target來區(qū)分不同的Handler。