
我們會經常使用 Handler 的 send 或 post 去安排一個延時、非延時或插隊執行的 Message,但對于這個 Message 到底什么時候執行以及為什么是這樣,鮮少細究過,
本文將一 一盤點并起底個中原理!
同時針對大家不太熟悉的異步 Message 和 IdleHandler,進行演示和原理普及,篇幅較大,慢慢享用,
非延時執行 Message
先在主執行緒創建一個 Handler 并復寫 Callback 處理,
private val mainHandler = Handler(Looper.getMainLooper()) { msg ->
Log.d(
"MainActivity",
"Main thread message occurred & what:${msg.what}"
)
true
}
不斷地發送期望即刻執行的 Message 和 Runnable 給主執行緒的 Handler,
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
testSendNoDelayedMessages()
}
private fun testSendNoDelayedMessages() {
Log.d("MainActivity","testSendNoDelayedMessages() start")
testSendMessages()
testPostRunnable()
Log.d("MainActivity","testSendNoDelayedMessages() end ")
}
private fun testSendMessages() {
Log.d("MainActivity","startSendMessage() start")
for (i in 1..10) {
sendMessageRightNow(mainHandler, i)
}
Log.d("MainActivity","startSendMessage() end ")
}
private fun testPostRunnable() {
Log.d("MainActivity","testPostRunnable() start")
for (i in 11..20) {
mainHandler.post { Log.d("MainActivity", "testPostRunnable() run & i:${i}") }
}
Log.d("MainActivity","testPostRunnable() end ")
}
什么時候執行?
公布下日志前,大家可以猜測下運行的結果,Message 或 Runnable 在 send 或 post 之后會否立即執行,不是的話,什么時候執行?
D MainActivity: testSendNoDelayedMessages() start
D MainActivity: startSendMessage() start
D MainActivity: startSendMessage() end
D MainActivity: testPostRunnable() start
D MainActivity: testPostRunnable() end
D MainActivity: testSendNoDelayedMessages() end
D MainActivity: Main thread message occurred & what:1
...
D MainActivity: Main thread message occurred & what:10
D MainActivity: testPostRunnable() run & i:11
...
D MainActivity: testPostRunnable() run & i:20
答案可能跟預想的略有出入,單一細想好像又是合理的:發送完的 Message 或 Runnable 不會立即執行,MessageQueue 的喚醒和回呼需要等主執行緒的其他作業完成之后才能執行,
為什么?
非延時的 sendMessage() 和 post() 最終仍然是呼叫 sendMessageAtTime() 將 Message 放入了 MessageQueue,只不過它的期待執行時間 when變成了 SystemClock.uptimeMillis(),即呼叫的時刻,
// Handler.java
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); // when 等于當前時刻
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
return enqueueMessage(queue, msg, uptimeMillis);
}
這些 Message 會按照 when 的先后排隊進入 MessageQueue 中,當 Message 滿足了條件會立即呼叫 wake,反之只是插入佇列而已,所以,上述的 send 或 post 回圈,會按照呼叫的先后挨個進入佇列,第一個 Message 會觸發 wake,
// MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
...
// 鑒于多執行緒往 Handler 里發送 Message 的情況
// 在向佇列插入 Message 前需要上鎖
synchronized (this) {
...
msg.markInUse(); // Message 標記正在使用
msg.when = when; // 更新 when 屬性
Message p = mMessages; // 拿到佇列的 Head
boolean needWake;
// 如果佇列為空
// 或者 Message 需要插隊(sendMessageAtFrontOfQueue)
// 又或者 Message 執行時刻比 Head 的更早
// 該 Message 插入到隊首
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
// 執行緒是否因為沒有可執行的 Message 正在 block 或 wait
// 是的話,喚醒
needWake = mBlocked;
} else {
// 如果佇列已有 Message,Message 優先級又不高,同時執行時刻并不早于隊首的 Message
// 如果執行緒正在 block 或 wait,或建立了同步屏障(target 為空),并且 Message 是異步的,則喚醒
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// 遍歷佇列,找到 Message 目標插入位置
for (;;) {
prev = p;
p = p.next;
// 如果已經遍歷到隊尾了,或 Message 的時刻比當前 Message 要早
// 找到位置了,退出遍歷
if (p == null || when < p.when) {
break;
}
// 如果前面決定需要喚醒,但佇列已有執行時刻更早的異步 Message 的話,先不喚醒
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
// 將 Message 插入佇列的目標位置
msg.next = p;
prev.next = msg;
}
// 需要喚醒的話,喚醒 Native 側的 MessageQueue
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
總結來講:
- 第一次 send 的 Message 在 enqueue 進 MessageQueue 的隊首后,通知 Native 側 wake
- 后續發送的其他 Message 或 Runnable 挨個 enqueue 進佇列
- 接著執行主執行緒的其他任務,比如日志的列印
- 空閑后 wake 完畢并在 next() 的下一次回圈里將隊首 Message 移除和回傳給 Looper 去回呼和執行
- 之后 loop() 開始讀取 MessageQueue 當前隊首 Message 的下一次回圈,當前時刻必然晚于 send 時候設定的when,所以佇列里的 Message 挨個出隊和回呼
結論
非延時 Message 并非立即執行,只是放入 MessageQueue 等待調度而已,執行時刻不確定,
MessageQueue 會記錄請求的時刻,按照時刻的先后順序進行排隊,如果 MessageQueue 中積攢了很多 Message,或主執行緒被占用的話,Message 的執行會明顯晚于請求的時刻,
延時執行 Message
延時執行的 Message 使用更為常見,那它又是何時執行呢?
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
testSendDelayedMessages()
}
private fun testSendDelayedMessages() {
Log.d("MainActivity","testSendDelayedMessages() start")
// 發送 Delay 2500 ms 的 Message
sendDelayedMessage(mainHandler, 1)
Log.d("MainActivity","testSendDelayedMessages() end ")
}
28:58.186 發送 Message,29:00.690 Message 執行,時間差為 2504ms,并非準確的 2500ms,
09-22 22:28:57.964 24980 24980 D MainActivity: onCreate()
09-22 22:28:58.186 24980 24980 D MainActivity: testSendDelayedMessages() start
// 發送 Message
09-22 22:28:58.186 24980 24980 D MainActivity: testSendDelayedMessages() end
// Message 執行
09-22 22:29:00.690 24980 24980 D MainActivity: Main thread message occurred & what:1
如果連續發送 10 個均延時 2500ms 的 Message 會怎么樣?
private fun testSendDelayedMessages() {
Log.d("MainActivity","testSendDelayedMessages() start")
// 連續發送 10 個 Delay 2500 ms 的 Message
for (i in 1..10) {
sendDelayedMessage(mainHandler, i)
}
Log.d("MainActivity","testSendDelayedMessages() end ")
}
第 1 個 Message 執行的時間差為 2505ms(39:56.841 - 39:54.336),第 10 個 Message 執行的時間差已經達到了 2508ms(39:56.844 - 39:54.336),
09-22 22:39:54.116 25104 25104 D MainActivity: onCreate()
09-22 22:39:54.336 25104 25104 D MainActivity: testSendDelayedMessages() start
09-22 22:39:54.337 25104 25104 D MainActivity: testSendDelayedMessages() end
09-22 22:39:56.841 25104 25104 D MainActivity: Main thread message occurred & what:1
09-22 22:39:56.842 25104 25104 D MainActivity: Main thread message occurred & what:2
09-22 22:39:56.842 25104 25104 D MainActivity: Main thread message occurred & what:3
..
09-22 22:39:56.842 25104 25104 D MainActivity: Main thread message occurred & what:8
09-22 22:39:56.842 25104 25104 D MainActivity: Main thread message occurred & what:9
09-22 22:39:56.844 25104 25104 D MainActivity: Main thread message occurred & what:10
為什么?
延時 Message 執行的時刻 when 采用的是發送的時刻和 Delay 時長的累加,基于此排隊進 MessageQueue,
// Handler.java
public final boolean postDelayed(Runnable r, int what, long delayMillis) {
return sendMessageDelayed(getPostMessage(r).setWhat(what), delayMillis);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
return enqueueMessage(queue, msg, uptimeMillis);
}
Delay Message 尚未抵達的時候,MessageQueue#next() 會將讀取佇列的時刻與 when 的差值,作為下一次通知 Native 休眠的時長,進行下一次回圈前,next() 還存在其他邏輯,導致 wake up 的時刻存在滯后,此外由于 wake up 后執行緒存在其他任務導致執行更加延后,
// MessageQueue.java
Message next() {
...
for (;;) {
...
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
...
if (msg != null) {
// 計算下一次回圈應當休眠的時長
if (now < msg.when) {
nextPollTimeoutMillis
= (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
...
}
} else {
...
}
...
}
...
}
}
結論
由于喚醒時長的計算誤差和回呼的任務可能占用執行緒,導致延時執行 Message 不是時間到了就會執行,其執行的時刻必然晚于 Delay 的時刻,
插隊執行 Message
Handler 還提供了 Message 插隊的 API:sendMessageAtFrontOfQueue() 和 postAtFrontOfQueue(),
在上述的 send 和 post 之后同時呼叫 xxxFrontOfQueue 的方法,Message 的執行結果會怎么樣?
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
testSendNoDelayedMessages()
testFrontMessages() // 立馬呼叫 FrontOfQueue 的方法
}
分別呼叫 sendMessageAtFrontOfQueue() 和 postAtFrontOfQueue() 的 API,
private fun testFrontMessages() {
Log.d("MainActivity","testFrontMessages() start")
testSendFrontMessages()
testPostFrontRunnable()
Log.d("MainActivity","testFrontMessages() end ")
}
private fun testSendFrontMessages() {
Log.d("MainActivity","testSendFrontMessages() start")
for (i in 21..30) {
sendMessageFront(mainHandler, i)
}
Log.d("MainActivity","testSendFrontMessages() end ")
}
private fun testPostFrontRunnable() {
Log.d("MainActivity","testPostFrontRunnable() start")
for (i in 31..40) {
mainHandler.postAtFrontOfQueue() { Log.d("MainActivity", "testPostFrontRunnable() run & i:${i}") }
}
Log.d("MainActivity","testPostFrontRunnable() end ")
}
當主執行緒的列印日志按序輸出后,Message 開始逐個執行,按照預想的一樣,FrontOfQueue 的 Message 會先執行,也就是最后一次呼叫這個 API 的最早回呼,
Front 的 Message 逆序執行完畢之后,普通的 Message 才按照請求的順序執行,
D MainActivity: testSendNoDelayedMessages() start
D MainActivity: startSendMessage() start
D MainActivity: startSendMessage() end
D MainActivity: testPostRunnable() start
D MainActivity: testPostRunnable() end
D MainActivity: testSendNoDelayedMessages() end
D MainActivity: testFrontMessages() start
D MainActivity: testSendFrontMessages() start
D MainActivity: testSendFrontMessages() end
D MainActivity: testPostFrontRunnable() start
D MainActivity: testPostFrontRunnable() end
D MainActivity: testFrontMessages() end
D MainActivity: testPostFrontRunnable() run & i:40
...
D MainActivity: testPostFrontRunnable() run & i:31
D MainActivity: Main thread message occurred & what:30
...
D MainActivity: Main thread message occurred & what:21
D MainActivity: Main thread message occurred & what:1
...
D MainActivity: Main thread message occurred & what:10
D MainActivity: testPostRunnable() run & i:11
... D MainActivity: testPostRunnable() run & i:20
怎么實作的?
原理在于 sendMessageAtFrontOfQueue() 或 postAtFrontOfQueue() 發送的 Mesage 被記錄的 when 屬性被固定為 0,
// Handler.java
public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
return enqueueMessage(queue, msg, 0); // 發送的 when 等于 0
}
public final boolean postAtFrontOfQueue(@NonNull Runnable r) {
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
從入隊函式可以看出,when 為 0 的 Message 會立即插入隊首,所以總會先得到執行,
// MessageQueue.java
enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
// 如果 Message 需要插隊(sendMessageAtFrontOfQueue)
// 則插入隊首
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
} else {
...
}
...
}
return true;
}
結論
sendMessageAtFrontOfQueue() 和 postAtFrontOfQueue() 的 API 通過將 when 預設為 0 進而插入 Message 至隊首,最終達到 Message 先得到執行的目的,
但需要注意的是,這將造成本來先該執行的 Message 被延后調度,對于存在先后關系的業務邏輯來說將可能造成順序問題,謹慎使用!
異步執行 Message
Handler 發送的 Message 都是同步的,意味著大家都按照 when 的先后進行排序,誰先到誰執行,
如果遇到優先級高的 Message 可以通過 FrontQueue 發送插隊 Message即可,但如果是希望同步的佇列停滯只執行指定 Message 的話,即 Message 異步執行,現有的 API 是不夠的,
事實上 Android 提供了同步屏障的機制來實作這一需求,不過主要面向的是系統 App 或 系統,App 可以通過反射來使用,
通過異步 Handler 實作
除了一般使用的 Handler 建構式以外,Handler 還提供了創建發送異步 Message 的專用建構式,通過該 Handler 發送的 Message 或 Runnable 都是異步的,我們將其稱為異步 Handler,
// Handler.java
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
@NonNull
public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) {
if (looper == null) throw new NullPointerException("looper must not be null");
if (callback == null) throw new NullPointerException("callback must not be null");
return new Handler(looper, callback, true);
}
我們啟動一個 HandlerThread 來測驗一下同步屏障的使用:分別構建一個普通 Handler和 異步 Handler,
private fun startBarrierThread() {
val handlerThread = HandlerThread("Test barrier thread")
handlerThread.start()
normalHandler = Handler(handlerThread.looper) { msg ->
Log.d(...)
true
}
barrierHandler = Handler.createAsync(handlerThread.looper) { msg ->
Log.d(...)
true
}
}
啟動 HandlerThread 并向兩個 Handler 各發送一個 Message,
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
startBarrierThread()
testNormalMessage()
testSyncBarrierByHandler()
}
private fun testNormalMessage() {
sendMessageRightNow(normalHandler, 1)
}
private fun testSyncBarrierByHandler() {
sendMessageRightNow(barrierHandler, 2)
}
是異步 Handler 的 Message 先執行嗎?非也,因為我們還沒有通知 MessageQueue 建立同步屏障!
09-24 23:02:19.032 28113 28113 D MainActivity: onCreate()
09-24 23:02:19.150 28113 28141 D MainActivity: Normal handler message occurred & what:1
09-24 23:02:19.150 28113 28141 D MainActivity: Barrier handler message occurred & what:2
除了發送異步Handler 發送異步 Message 以外,需要通過反射事先建立起同步屏障,
注意:建立同步屏障必須早于需要屏蔽的同步 Message,否則無效,后面的原理會提及,
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
startBarrierThread()
// 建立一個同步屏障
postSyncBarrier(barrierHandler.looper)
testNormalMessage()
testSyncBarrierByHandler()
}
private fun postSyncBarrier(looper: Looper) {
Log.d(...)
val method: Method = MessageQueue::class.java.getDeclaredMethod("postSyncBarrier")
barrierToken = method.invoke(looper.queue) as Int
}
這樣子便可以看到,異步 Message 執行了,而且同步 Message 永遠得不到執行,
09-24 23:11:36.176 28600 28600 D MainActivity: onCreate()
09-24 23:11:36.296 28600 28600 D MainActivity: Add sync barrier
09-24 23:11:36.300 28600 28629 D MainActivity: Barrier handler message occurred & what:2
原因在于建立的同步屏障尚未移除,永遠只處理佇列里的異步 Message,想要讓同步 Message 恢復執行的話 remove 同步屏障即可,同樣也需要反射!
我們在異步 Handler 執行結束后移除同步屏障,
private fun startBarrierThread() {
...
barrierHandler = Handler.createAsync(handlerThread.looper) { msg ->
Log.d(...)
// 移除同步屏障
removeSyncBarrier(barrierHandler.looper)
true
}
}
fun removeSyncBarrier(looper: Looper) {
Log.d(...)
val method = MessageQueue::class.java
.getDeclaredMethod("removeSyncBarrier", Int::class.javaPrimitiveType)
method.invoke(looper.queue, barrierToken)
}
可以看到同步 Message 恢復了,
09-24 23:10:31.533 28539 28539 D MainActivity: onCreate()
09-24 23:10:31.652 28539 28568 D MainActivity: Barrier handler message occurred & what:2
09-24 23:10:31.652 28539 28568 D MainActivity: Remove sync barrier
09-24 23:10:31.653 28539 28568 D MainActivity: Normal handler message occurred & what:1
通過異步 Message 實作
沒有專用的異步 Handler 的時候,可以向普通 Handler 發送一個 isAsync 屬性為 true 的 message,效果和異步 Handler 是一樣的,當然這種方式仍舊需要建立同步屏障,
在原有的發送 Message 的函式里加入 isAsync 的多載引數,
private fun sendMessageRightNow(handler: Handler, what: Int, isAsync: Boolean = false) {
Message.obtain().let {
it.what = what
it.isAsynchronous = isAsync
handler.sendMessage(it)
}
}
向普通 Handler 發送異步 Message,
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
testNormalMessage()
// 改用 Message 方式發送異步 Message
testSyncBarrierByMessage()
}
private fun testSyncBarrierByMessage() {
sendMessageRightNow(normalHandler, 2, true)
}
同樣記得在異步 Message 收到后移除同步屏障,
private fun startBarrierThread() {
...
normalHandler = Handler(handlerThread.looper) { msg ->
Log.d(...)
if (2 == msg.what) removeSyncBarrier(barrierHandler.looper)
true
}
}
結果和異步 Handler 的方式一致,
09-24 23:58:05.801 29040 29040 D MainActivity: onCreate()
09-24 23:58:05.923 29040 29040 D MainActivity: Add sync barrier
09-24 23:58:05.923 29040 29070 D MainActivity: Normal handler message occurred & what:2
09-24 23:58:05.924 29040 29070 D MainActivity: Remove sync barrier
09-24 23:58:05.924 29040 29070 D MainActivity: Normal handler message occurred & what:1
原理
先來看一下同步屏障是怎么建立的,
// MessageQueue.java
// 默認是呼叫的時刻開始建立屏障
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
// 同步屏障支持指定開始的時刻
// 默認是呼叫的時刻,而 0 表示?
private int postSyncBarrier(long when) {
synchronized (this) {
// 同步屏障可以建立多個,用計數的 Token 變數識別
final int token = mNextBarrierToken++;
// 獲取一個屏障 Message
// 其 target 屬性為空
// 指定 when 屬性為屏障的開始時刻
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
// 將 Token 存入屏障 Message
// 用以識別對應的同步屏障
msg.arg1 = token;
// 按照 when 的先后
// 找到屏障 Message 插入佇列的適當位置
// 所以,如果同步屏障的建立呼叫得晚
// 那么在它之前的 Message 無法阻攔
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
// 將屏障 Message 插入
if (prev != null) {
msg.next = p;
prev.next = msg;
} else {
// 如果佇列尚無 Message
// 或隊首的 Message 時刻
// 都比屏障 Message 要晚的話
// 將屏障 Message 插入隊首
msg.next = p;
mMessages = msg;
}
// 回傳上面的 Token 給呼叫端
// 主要用于移除對應的屏障
return token;
}
}
再來看下異步 Message 如何執行,
// MessageQueue.java
Message next() {
...
for (;;) {
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 隊首是屏障 Message 的話
// 遍歷找到下一個異步 Message
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
// 沒有建立同步屏障且隊里有 Message
// 或者
// 建立了同步屏障下且找到了異步 Message
if (msg != null) {
// 如果當前時間尚早于目標執行時刻
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 回傳
msg.next = null;
msg.markInUse();
return msg;
}
} else {
// 隊里尚無 Message
// 或建立了同步屏障,但尚無異步 Message
// 無限休眠
nextPollTimeoutMillis = -1;
}
...
}
...
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
最后看一下同步屏障如何移除,
// MessageQueue.java
// 需要傳入 add 時回傳的 Token
public void removeSyncBarrier(int token) {
synchronized (this) {
Message prev = null;
Message p = mMessages;
// 遍歷佇列直到找到 token 吻合的屏障 Message
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
// 如果沒找到會拋出例外
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
// 將屏障 Message 移除
// 如果屏障 Message 不在隊首的話
// 無需喚醒
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
// 屏障 Message 在隊首
// 且新的隊首存在且不是另一個屏障的話
// 需要立即喚醒
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
p.recycleUnchecked();
// 喚醒以立即處理后面的 Message
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
對原理進行簡單的總結:
- 同步屏障的建立:按照呼叫的時刻 when 在合適的位置放入一個屏障 Message(
target屬性為 null)來實作,同時得到標識屏障的計數token存入屏障 Message - 讀取佇列的時候發現存在屏障 Message 的話,會遍歷佇列并回傳最早執行的異步 Message
- 同步屏障的移除:按照 token 去佇列里找到匹配的屏障 Message 進行出隊操作,如果出隊后隊首存在 Message 且非另一個同步屏障的話,立即喚醒
looper 執行緒
結論和應用
結論:
- 可以通過異步 Handler,也可以通過異步 Message 兩種方式向 MessageQueue 添加異步 Message
- 但都需要事先建立同步屏障,屏障的建立時間必須在阻攔的 Message 發出之前
- 可以建立多個同步屏障,將按照指定的時刻排隊,通過計數 Token 進行識別
- 同步屏障使用完之后記得移除,否則后續的 Message 永遠阻塞
和插隊執行 Message 的區別:
- 插隊 Message 只能確保先執行,完了后續的 Message 還得執行
- 異步 Message 則不同,同步屏障一旦建立將保持休眠,直到異步 Message 抵達,只有同步屏障被撤銷,后續 Message 才可恢復執行
應用:
AOSP 系統中使用異步 Message 最典型的地方要屬螢屏重繪,重繪的 Message 不希望被主執行緒的 Message 佇列阻塞,所以在發送重繪 Message 之前都會建立一個同步屏障,確保重繪任務優先執行,
// ViewRootImpl.java
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
屏障建立之后發送異步 Message,
// Choreographer.java
private void postCallbackDelayedInternal(...) {
synchronized (mLock) {
...
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
IdleHandler ”Message“
MessageQueue 提供的 IdleHandler 可以讓佇列在空閑的時候回呼(queueIdle())指定的邏輯,它本質上不是 Message 型別,但它在 MessageQueue 里調度的時候類似于 Message 的邏輯,姑且將它也理解成一種特殊的 ”Message“,
使用上很簡單,呼叫 MessageQueue 的 addIdleHandler() 添加實作即可,執行完之后無需再度執行的話需要呼叫 removeIdleHandler() 移除,或在回呼里回傳 false,
override fun onCreate(savedInstanceState: Bundle?) {
...
testIdleHandler()
}
private fun testIdleHandler() {
Log.d("MainActivity","testIdleHandler() start")
mainHandler.looper.queue.addIdleHandler {
Log.d("MainActivity", "testIdleHandler() queueIdle callback")
false
}
Log.d("MainActivity","testIdleHandler() end ")
}
可以看到 addIdleHandler 呼叫之后并沒有立即執行,而是過了幾百 ms,queueIdle() 才得到了執行,
09-23 22:56:46.130 7732 7732 D MainActivity: onCreate()
09-23 22:56:46.281 7732 7732 D MainActivity: testIdleHandler() start
09-23 22:56:46.281 7732 7732 D MainActivity: testIdleHandler() end
09-23 22:56:46.598 7732 7732 D MainActivity: testIdleHandler() queueIdle callback
如果在 addIdleHandler 呼叫之后接著發送一串非延時 Message,queueIdle() 是先執行還是后執行呢?
override fun onCreate(savedInstanceState: Bundle?) {
...
testIdleHandler()
testSendMessages()
}
結果顯示一堆 Message 執行完了之后,仍舊過了幾百 ms,queueIdle() 才得到了執行,
09-23 23:07:50.639 7926 7926 D MainActivity: onCreate()
09-23 23:07:50.856 7926 7926 D MainActivity: testIdleHandler() start
09-23 23:07:50.856 7926 7926 D MainActivity: testIdleHandler() end
09-23 23:07:50.856 7926 7926 D MainActivity: startSendMessage() start
09-23 23:07:50.857 7926 7926 D MainActivity: startSendMessage() end
09-23 23:07:50.914 7926 7926 D MainActivity: Main thread message occurred & what:1
...
09-23 23:07:50.916 7926 7926 D MainActivity: Main thread message occurred & what:10
09-23 23:07:51.132 7926 7926 D MainActivity: testIdleHandler() queueIdle callback
上述結果也可以理解,MessageQueue 里仍有一堆 Message 等待處理,并非空閑狀態,所以需要執行完之后才有機會回呼 queueIdle() ,
那如果發送的是延時 Message 呢?
override fun onCreate(savedInstanceState: Bundle?) {
...
testIdleHandler()
testSendDelayedMessages()
}
因為發送的是延時 Message,MessageQueue 暫時是空閑的,會先將 IdleHandler 取出來處理,
09-23 23:21:36.135 8161 8161 D MainActivity: onCreate()
09-23 23:21:36.339 8161 8161 D MainActivity: testIdleHandler() start
09-23 23:21:36.340 8161 8161 D MainActivity: testIdleHandler() end
09-23 23:21:36.340 8161 8161 D MainActivity: testSendDelayedMessages() start
09-23 23:21:36.340 8161 8161 D MainActivity: testSendDelayedMessages() end
09-23 23:21:36.729 8161 8161 D MainActivity: testIdleHandler() queueIdle callback
09-23 23:21:38.844 8161 8161 D MainActivity: Main thread message occurred & what:1
...
09-23 23:21:38.845 8161 8161 D MainActivity: Main thread message occurred & what:10
上面的 queueIdle() 回傳了 false 確保處理后 Handler 得到了移除,
但如果回傳 true 且沒有呼叫 removeIdleHandler() 的話,后續空閑的時候 Handler 還會被執行,這點需要留意!
private fun testIdleHandler() {
mainHandler.looper.queue.addIdleHandler {
...
true // false
}
}
queueIdle() 因為沒被移除的緣故被回呼了多次,源自于 Looper 沒執行完一次 Message 后發現尚無 Message 的時候都會回呼一遍 IdleHandler,直到佇列一直沒有 Message 到來,
09-23 23:24:04.765 8226 8226 D MainActivity: onCreate()
09-23 23:24:05.010 8226 8226 D MainActivity: testIdleHandler() start
09-23 23:24:05.011 8226 8226 D MainActivity: testIdleHandler() end
09-23 23:24:05.368 8226 8226 D MainActivity: testIdleHandler() queueIdle callback
09-23 23:24:05.370 8226 8226 D MainActivity: testIdleHandler() queueIdle callback
09-23 23:24:05.378 8226 8226 D MainActivity: testIdleHandler() queueIdle callback
09-23 23:24:05.381 8226 8226 D MainActivity: testIdleHandler() queueIdle callback
09-23 23:24:05.459 8226 8226 D MainActivity: testIdleHandler() queueIdle callback
那如果 add 完不移除的 IdleHandler 之后,發送一個延時 Message,那便會導致空閑訊息多執行一遍,
override fun onCreate(savedInstanceState: Bundle?) {
...
testIdleHandler()
sendDelayedMessage(mainHandler, 1)
}
09-23 23:31:53.928 8620 8620 D MainActivity: onCreate()
09-23 23:31:54.042 8620 8620 D MainActivity: testIdleHandler() start
09-23 23:31:54.042 8620 8620 D MainActivity: testIdleHandler() end
09-23 23:31:54.272 8620 8620 D MainActivity: testIdleHandler() queueIdle callback
09-23 23:31:54.273 8620 8620 D MainActivity: testIdleHandler() queueIdle callback
09-23 23:31:54.278 8620 8620 D MainActivity: testIdleHandler() queueIdle callback
09-23 23:31:54.307 8620 8620 D MainActivity: testIdleHandler() queueIdle callback
09-23 23:31:54.733 8620 8620 D MainActivity: testIdleHandler() queueIdle callback
09-23 23:31:56.546 8620 8620 D MainActivity: Main thread message occurred & what:1
09-23 23:31:56.546 8620 8620 D MainActivity: testIdleHandler() queueIdle callback
為什么?
queueIdle() 的回呼由 MessageQueue#next() 回呼,
// MessageQueue.java
Message next() {
...
// 回圈的初次將待處理 IdleHandler 計數置為 -1
// 保證第一次可以檢查 Idle Handler 的存在和呼叫
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
...
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 隊首的 Message 且建立了同步屏障的話,尋找下一個異步 Message
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
// 找到了合適的 Message
if (msg != null) {
// 如果當前時間尚早于目標執行時刻
// 設定休眠的超時時間,即當前時間與目標時刻的差值
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
msg.next = null;
msg.markInUse();
return msg;
}
} else {
// 隊里尚無合適的 Message
// 進入無限休眠
nextPollTimeoutMillis = -1;
}
// 如果正在退出 Looper,結束回圈并回傳 null
// 將促使 loop() 退出
if (mQuitting) {
dispose();
return null;
}
// 如果沒有合適的 Message 且 Looper 沒有退出
// 檢查是否有 Idle Handler 需要處理
// 讀取 Idle Handler 串列
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
// 如果暫時沒有 Idle Handler 需要處理,則進入下一次回圈
// 為使下次回圈如果出現新的 Idle Handler 能有機會執行
// 不重置計數器,仍為初始值 -1
if (pendingIdleHandlerCount <= 0) {
mBlocked = true;
continue;
}
// 如果 IdleHandler 存在則拷貝到待處理串列
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 遍歷待處理 Idle Handlers
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null;
boolean keep = false;
try {
// 逐個回呼 queueIdle()
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
// 回呼回傳 false,則將其移除出 Idle 串列
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// 處理完之后重置 IdleHandler 的計數
// 保證下次回圈不會重復處理 IdleHandler
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
有幾點細節需要留意:
- next() 回圈的第一次將 count 置為 -1,確保佇列空閑的時候必然有機會處理 IdleHandler
- 如果暫無 IdleHandler 可以處理直接進入下一次回圈,并且保留 count 的處置,確保下次回圈可以檢查是否有新的 IdleHandler 加入進來
- IdleHandler 正常處理結束之后,避免下次回圈重復處理,會將 count 置為 0,保證下次不再檢查,注意:是下次回圈,不是永久不檢查
結論和應用
結論:
IdleHandler 可以實作 MessageQueue 空閑狀態下的任務執行,比如做一些啟動時的輕量級初始化任務,但由于其執行的時機依賴于佇列的 Message 狀態,不太可控,謹慎使用!
應用:AOSP 原始碼里有不少地方使用了 IdleHandler 機制,比如 ActivityThread 使用它在空閑的狀態下進行 GC 回收處理,
// ActivityThread.java
final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
purgePendingResources();
return false;
}
}
void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
Looper.myQueue().addIdleHandler(mGcIdler);
}
mH.removeMessages(H.GC_WHEN_IDLE);
}
void unscheduleGcIdler() {
if (mGcIdlerScheduled) {
mGcIdlerScheduled = false;
Looper.myQueue().removeIdleHandler(mGcIdler);
}
mH.removeMessages(H.GC_WHEN_IDLE);
}
術語總結
非延時、延時以及插隊執行這幾種 Message 大家使用較多,無需贅述,但其他幾個冷僻的 Message 術語需要總結一下,供大家快速對比和加深理解,
| Message 術語 | 介紹 |
|---|---|
| 異步 Message | isAsync 屬性為 true 需要異步執行的 Message,需要配合同步屏障使用 |
| 異步 Handler | 專門發送異步 Message 的 Handler |
| 屏障 Message | target 為空并持有 token 資訊的 Message 實體放入佇列,作為同步屏障的起點 |
| 同步屏障 | 在 MessageQueue 指定時刻插入屏障 Message 確保只有異步 Message 執行的機制 |
| 空閑 IdleHandler | 用于在 MessageQueue 空閑的時候回呼的處理介面,若不移除每次佇列空閑了均會執行 |
結語
上述對于各種 Message 和 IdleHandler 做了演示和原理闡述,相信對于它的細節有了更深的了解,
下面來進行一個簡單的總結:
非延時執行 Message:并非立即執行,而是按照請求的時刻進行排隊和調度,最終取決于佇列的順序和主執行緒是否空閑延時執行 Message:也并非在 Delay 的時刻立即執行,執行時刻受喚醒誤差和執行緒任務阻塞的影響必然晚于 Delay 時刻插隊執行 Mesage:同樣并非立即執行,而是每次都將任務放在了隊首,達到先執行的目的,但打亂了執行順序存在邏輯隱患異步Message:系統使用居多,App 則需反射,通過這種機制可插隊執行同時確保其他 Message 阻塞,學習一下IdleHandler“Message”:系統多有使用,實作 MessageQueue 空閑狀態下的任務執行,但執行時機不可控,最好執行完之后便移除,謹慎使用
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/303077.html
標籤:其他
上一篇:Android 從零開始了解Google訂閱服務(一)
下一篇:??【Android精進之路-05】怎么創建Activity,如何啟動另一個Activity,干貨滿滿,建議收藏??
