該文章屬於《Android Handler機制之》系列文章,若是想了解更多,請點擊 《Android Handler機制之總目錄》安全
在上幾篇文中咱們介紹了整個消息的循環機制以及消息的回收。如今咱們來看看整麼退出循環消息隊列。(到如今爲止,整個Android Handler機制快要接近尾聲了。不知道你們看了整個系列的文章,有沒有對Handler機制有個深一點的瞭解。若是對你有所幫助,我也感到十分的開心與自豪~~~)。bash
要想結束循環消息隊列,須要調用Loooper的quitSafely()或quit()方法,具體代碼以下所示:oop
public void quitSafely() { mQueue.quit(true);}
public void quit() { mQueue.quit(false); }
複製代碼
而該兩個方法的內部,都會調用Loooper中對應的MessageQueue的quit(boolean safe)方法。查看quit(boolean safe)方法:post
void quit(boolean safe) {
if (!mQuitAllowed) {//注意,主線程是不能退出消息循環的
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {//若是當前循環消息已經退出了,直接返回
return;
}
mQuitting = true;//該標記十分重要,十分重要
if (safe) {//若是是安全退出
removeAllFutureMessagesLocked();
} else {//若是不是安全退出
removeAllMessagesLocked();
}
nativeWake(mPtr);
}
}
複製代碼
在MessageQueue的quit(boolean safe)方法中,會將mQuitting (用於判斷當前消息隊列是否已經退出,該標誌位十分有用,下文會提到)置爲true,同時會根據當前是否安全退出的標誌 (safe)來走不一樣的邏輯,若是安全則走removeAllFutureMessagesLocked()方法,若是不是安全退出則走removeAllMessagesLocked()方法。下面分別對這兩個方法進行討論。ui
private void removeAllMessagesLocked() {
Message p = mMessages;//消息隊列中的頭節點
while (p != null) {
Message n = p.next;
p.recycleUnchecked();//回收消息
p = n;
}
mMessages = null;//將消息隊列中的頭節點置爲null
}
複製代碼
非安全退出其實很簡單,就是遍歷消息隊列中的消息(消息隊列內部結構是鏈表)將全部消息隊列中的消息所有回收。同時將MessageQueue中的mMessages (消息隊列中的頭消息)置爲null,其中關於Message的recycleUnchecked()方法,若是你對該方法不是很熟悉,建議先閱讀《Android Handler機制之Message及Message回收機制 》。關於非安全退出時,消息隊列中的回收示意圖以下所示:this
上文中,咱們描述了在非安全退出時MessageQueue中僅僅進行了消息的回收,而並無真正涉及到循環消息隊列的退出,如今咱們就來看一看息循環退出的具體邏輯。咱們都知道整個消息循環的消息得到,都是經過Loooper中的loop()方法,具體代碼以下所示:spa
public static void loop() {
final Looper me = myLooper();
//省略部分代碼...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
//若是沒有消息,那麼說明當前消息隊列已經退出
return;
}
//省略部分代碼
}
}
複製代碼
從代碼中咱們能夠退出,整個loop()方法的結束,會與MessageQueue的next()方法相關,若是next()方法獲取的msg爲null,那麼Looper中的loop方法也直接結束。那麼整個消息循環也退出了。那接下來咱們來查看Message 中的next()方法,具體代碼以下所示:線程
Message next() {
//省略部分代碼...
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
synchronized (this) {
//省略部分代碼..
if (mQuitting) {
dispose();
return null;
}
//省略部分代碼..
}
}
複製代碼
在Message中的next()方法中,若是mQuitting爲true,該方法會直接就返回了。**你們還記得咱們mQuitting是何時置爲true的嗎?**對!就是咱們調用Loooper的quitSafely()或quit()方法(也就是調用MessageQueue.quit(boolean safe))時,會將mQuitting置爲true。code
那麼到如今整個循環消息隊列的退出的邏輯就清楚了。主要分爲如下幾個步驟:cdn
如今爲止,咱們已經基本瞭解了整個循環消息隊列退出的流程了。在瞭解了非安全退出的方法以後,咱們再來看看安全退出時,涉及到的邏輯操做。上文咱們已經提到過了,當Looper調用quitSafely()方法時,內部會走MessagQueue的removeAllFutureMessagesLocked()。具體代碼以下:
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;//當前隊列中的頭消息
if (p != null) {
if (p.when > now) {//判斷時間,若是Message的取出時間比當前時間要大直接移除
removeAllMessagesLocked();
} else {
Message n;
for (;;) {//繼續判斷,取隊列中全部大於當前時間的消息
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {//將全部全部大於當前時間的消息的消息回收
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
複製代碼
觀察上訴代碼,在該方法中,會判斷當前消息隊列中的頭消息的時間是否大於當前時間,若是大於當前時間就會removeAllMessagesLocked()方法(也就是回收所有消息),反之,則回收部分消息,同時沒有被回收的消息任然能夠被取出執行。具體示意圖以下所示:
當使用安全退出循環消息隊列時,整個退出邏輯與非安全退出有必定的區別。在上文中咱們說過。當安全退出時,程序會判斷消息隊列會根據消息中的message.when來判斷是否回收消息。那麼在消息隊列中沒有被回收的消息是仍然能被取出執行的。具體代碼以下所示:
Message next() {
//省略部分代碼...
for (;;) {
//省略部分代碼...
synchronized (this) {
//省略部分代碼...
Message prevMsg = null;
Message msg = mMessages;
if (msg != null) {
if (now < msg.when) {
//省略部分代碼...
} 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);
msg.markInUse();
return msg;
}
}
//省略部分代碼...
// 第二處,退出循環消息隊列
if (mQuitting) {
dispose();
return null;
}
//省略部分代碼...
}
}
複製代碼
從上述代碼中咱們能夠明顯的看見,當咱們的消息隊列中仍然有消息的時候,是會取出消息並返回的。無論mQuitting的值是否設置爲true。那麼當整個消息隊列中的消息取完之後。纔會走返回null(圖上代碼 第二處)。
其實有不少小朋友們確定會關注,"當我整個循環消息隊列退出的時候,若是我仍然使用Handler發送消息,那麼個人消息去那裏了呢,是被拋棄了,仍是有什麼特殊處理呢?",下面咱們就帶着這些疑惑來看看Handler機制中具體的處理。
咱們都知道當調用Handler發送消息的時候,最終走的方法就是enqueueMessage()方法,其中該方法內部又會走MessageQueue的enqueueMessage(Message msg, long when)方法。具體代碼以下所示:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
複製代碼
MessageQueue的enqueueMessage(Message msg, long when)方法,具體代碼以下所示:
boolean enqueueMessage(Message msg, long when) {
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();//回收消息
return false;//返回該消息沒有加入消息隊列的標誌
}
//省略部分代碼。。。
}
複製代碼
經過觀察MessageQueue的enqueueMessage(Message msg, long when)方法,咱們能得出該方法內部會判斷當前循環消息隊裏是否退出,若是已經結束,那麼會將Handler發送的消息回收,同時會返回該消息沒有加入消息隊列的標誌(return false)。