在閱讀分析Android 繪制原理的原始碼時,挺多文章指出繪制訊息message的執行肯定優先所有的message,這個優先機制是利用Handler的同步屏障機制和異步訊息,但是這些機制對開發者不開放的,相關方法也標注為@hide,我猜測如果對開發者開放,那么為了使得自己的訊息優先執行,繪制的訊息不能搶先執行,很有可能會導致卡頓.分析同步屏障和異步訊息的原理文章已經很多,大牛們分析的肯定比我透徹很多,哈哈.
但是實戰中,會有需求,雖然繪制這一類有同步屏障機制的訊息的還是優先執行.但在之后的訊息中我們是否可以讓自己的訊息優先執行呢,查閱Handler的原始碼中,有如下幾個方法可以實作這樣的效果
Handler 類:
public final boolean postAtFrontOfQueue(@NonNull Runnable r) {
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
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, 0);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
二其余發送訊息的API 無論是post,sendMessage等,最終也會呼叫enqueueMessage(),不過區別就在enqueueMessage()方法中的第三個引數uptimeMillis,postAtFrontOfQueue最終呼叫sendMessageAtFrontOfQueue,所以只要看sendMessageAtFrontOfQueue呼叫enqueueMessage方法,傳入uptimeMillis是0,而其余的要么是系統當前時間,要么系統當前時間加延遲的時間.
Handler 類
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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;
}
// uptimeMillis是系統時間或者系統時間加上延遲的時間
return enqueueMessage(queue, msg, uptimeMillis);
}
看下MessageQueue的enqueueMessage方法傳0和不傳0的區別
MessageQueue類
boolean enqueueMessage(Message msg, long when) {
//when 接收了uptimeMillis的值
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
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;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
//當when == 0的時候,是走這個邏輯的,相當于msg作為新的鏈表的頭結點
//這里還有一個when<p.when的條件,什么時候觸發,后面在分析
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//else的執行中邏輯當正常post,sendMessage的時候,when接收的時間值是大于等于系統
// 時間,肯定也比頭結點所在的時間要大
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
分析得到,如果uptimeMillis是0的話,那么會讓當前的訊息成為頭結點.所以這個訊息除了采用同步屏障的異步訊息外,肯定優先執行.理論上是這樣,實操中是如此嗎?demo過于簡單,只貼出核心方法.
fun postDemos(){
//1 post
handler.post {
Log.i(TAG,"1 post")
}
//2 send message
val msg = Message.obtain()
msg.what = 1
msg.obj = "2 send message"
handler.sendMessage(msg)
//3 post
handler.post {
Log.i(TAG,"3 post")
}
//4 post
handler.post {
Log.i(TAG,"4 post")
}
//5 send message
val msg1 = Message.obtain()
msg1.what = 1
msg1.obj = "5 send message"
handler.sendMessage(msg1)
//6 postFirset
handler.postAtFrontOfQueue {
Log.i(TAG,"6 postFirst")
}
//7 sendMessageAtTime
val msg2 = Message.obtain()
msg2.what = 1
msg2.obj = "7 send message"
handler.sendMessageAtFrontOfQueue(msg2)
}
這個方法輸出的日志是這樣的
2021-09-19 08:48:45.251 6532-6532/com.hy.learndemo I/MainActivity: 7 send message
2021-09-19 08:48:45.251 6532-6532/com.hy.learndemo I/MainActivity: 6 postFirst
2021-09-19 08:48:45.252 6532-6532/com.hy.learndemo I/MainActivity: 1 post
2021-09-19 08:48:45.252 6532-6532/com.hy.learndemo I/MainActivity: 2 send message
2021-09-19 08:48:45.252 6532-6532/com.hy.learndemo I/MainActivity: 3 post
2021-09-19 08:48:45.252 6532-6532/com.hy.learndemo I/MainActivity: 4 post
2021-09-19 08:48:45.252 6532-6532/com.hy.learndemo I/MainActivity: 5 send message
很明顯,postAtFrontOfQueue,sendMessageAtFrontOfQueue發送的訊息雖然在最后入佇列,但優先執行,不過7比6先執行是怎么回事,那是因為sendMessageAtFrontOfQueue后執行,所以最后的頭結點就是它發送的訊息.在分析enqueueMessage方法執行邏輯的時候,留一個問題,什么時候會觸發when<p.when.除了0之外,也就是說存在一種情況需要發送的訊息的時間值還能比頭結點的時間還小,還記得sendMessageAtTime這個方法嗎,這個方法是public方法,沒有標注為@hide,所以我們也能用,重新貼一下方法
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);
}
傳入的 uptimeMillis并沒有做小于當前時間的校驗,是支持傳入小于當前時間的訊息.所以優先執行訊息除了剛才說的postAtFrontOfQueue,sendMessageAtFrontOfQueue之外,還有一個方法就是呼叫sendMessageAtTime,傳入比當前時間小的時間戳.不過不建議直接呼叫sendMessageAt()方法,呼叫現成的postAtFrontOfQueue或者sendMessageAtFrontOfQueue.相反的sendMessageAtTime方法中,如果傳入比當前時間值大,效果就是執行某個時間執行訊息.所以Handler機制對訊息執行的優先級以及時間自由度還是挺大的
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/302085.html
標籤:其他
上一篇:Win11上手初體驗,文末附Win10升級Win11方法
下一篇:Android注冊登錄頁面
