
我們會使用 quit() 或 quitSafely() 終止 Looper 執行緒的輪循,其背后的原理和細節,今日一并了解下,
quit()
子執行緒可以手動呼叫 quit() 退出輪循,
// Looper.java
public void quit() {
// 默認是不安全的退出
mQueue.quit(false);
}
Looper 的呼叫實則由 MessageQueue 全權處理,包括:標記正在退出,并清空 Mesage,最后喚醒執行緒去處理,
// MessageQueue.java
void quit(boolean safe) {
...
synchronized (this) {
...
mQuitting = true; // 標記 quitting
// 不安全的退出將回收佇列中所有 Message,并清空佇列
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// 通知喚醒執行緒
nativeWake(mPtr);
}
}
- 退出的標記將導致后續的 sendMessage() 或 postRunnable() 將失效,直接回傳 false,
// MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
...
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();
// quitting flag 還會導致后續的 Message send 失敗
return false;
}
...
}
return true;
}
- 默認是清空佇列里所有 Message,包括時間正好抵達的 Message 都無法處理,不太友好,
// MessageQueue.java
// 無論 when 是否抵達一刀切,可能當前時刻本該執行的 Message 也被剔除,無法執行
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
- 喚醒的執行緒將進入讀取佇列的下一次回圈,因為佇列已無 Message,將回傳 null,
// MessageQueue.java
Message next() {
...
for (;;) {
...
synchronized (this) {
...
// 引發取 Message 的下次回圈回傳 null
if (mQuitting) {
dispose();
return null;
}
...
}
...
}
}
- loop() 拿到的 Message 為空,死回圈退出,執行緒結束,
// Looper.java
public static void loop() {
...
for (;;) {
Message msg = queue.next();
if (msg == null) {
// loop() 拿到 null 則退出
return;
}
...
}
}
更推薦 quitSafely()
更推薦 quitSafely(),因其只會剔除執行時刻晚于當前時刻的 Message:保證 quit 呼叫的那刻,滿足條件的能保留在佇列當中,而不滿足的 Message 則全部出隊,
// MessageQueue.java
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
// 如果隊首 Message 的執行時刻仍晚于當前時刻,那么全部清空
if (p.when > now) {
removeAllMessagesLocked();
} else {
// 否則遍歷佇列,篩選需要剔除的 Message
Message n;
for (;;) {
n = p.next;
// 沒有更晚的 Message,均不需要剔除,直接回傳
if (n == null) {
return;
}
// 找到佇列中最前一個晚于當前時刻的 Message
if (n.when > now) {
break;
}
p = n;
}
// 前一個 Message 后全部出隊
p.next = null;
// 將最前一個晚于當前時刻的 Message 及之后的 Message 回收
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
- 喚醒的 next() 回圈將取出留在佇列里的 Message 進行處理,同時更新佇列
- 下一次 next 取不到 Message,會因為 quitting 的 flag 回傳 null,告知 loop() 死回圈退出
Message next() {
...
for (;;) {
...
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
...
if (msg != null) {
// 佇列里的 Message 早于當前時間,進入else
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
mBlocked = false;
// Message 出隊并更新指向
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
// 隊首后移一個節點
mMessages = msg.next;
}
// 拿到了 Message,并交給 Looper 回呼
msg.next = null;
msg.markInUse();
return msg;
}
} else {
...
}
...
}
...
}
}
主執行緒的 Looper 如何退出
主執行緒創建的 Looper 指定了不允許 quit,即不可以手動呼叫 quit,那么如何 quit 的? Todo
// Looper.java
public static void prepareMainLooper() {
prepare(false);
...
}
private static void prepare(boolean quitAllowed) {
...
sThreadLocal.set(new Looper(quitAllowed));
}
// Main Looper 初始化的時候指定了不允許退出
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
...
}
如果強行在主執行緒里呼叫了 quit(),會發生例外:
java.lang.IllegalStateException: Main thread not allowed to quit.
// MessageQueue.java
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
...
}
-
那主執行緒如何退出呢?
不需要退出,在記憶體不足的時候 App 由 AMS 直接回收行程,
-
更不用手動 quit,原因?
主執行緒 ActivityThread 極為重要,承載了 Application、Activity、Service 等組件的生命周期,即便某個組件結束了它仍有存在的必要,
換言之,ActivityThread 的作用域超過了這些組件,不該由這些組件去處理它的結束,
比如,Activity destroy 了,ActivityThread 仍然要處理其他 Activity 或 Service 等組件的事務,不能結束,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/301310.html
標籤:其他
下一篇:【iOS】如何在 NSViewController、NSView 中關閉當前 NSWindowController ?
