MessageQueue&Message Pool—訊息的讀寫、刪除和回收
阿新 • • 發佈:2019-01-25
如有原始碼,版本為android6.0
訊息的讀寫和刪除對應的類是MessageQueue,這裡的寫也伴隨著刪除,回收有個抽象的概念Global Pool或者Message Pool,前者obtain()方法註釋有提到,後者我感覺更形象。當然回收是在Looper的loop()方法中。
1、MessageQueue的資料結構
翻譯為訊息佇列,但實際的資料結構是單向連結串列結構,下文中仍以訊息佇列稱之;
Message中有個重要的屬性Message next;next指向另外一個Message。在MessageQueue中Message A的next指向B,B的next指向C;這樣 A B C就組成了單向連結串列結構,它們是以時間排序的,其中A為header,表示最先要處理的訊息,C是最後要處理的訊息,這個header是MessageQueue的一個屬性,這樣有了這個Header就擁有整個訊息連結串列。
2、MessageQueue三種操作
插入、獲取、刪除;對應方法為enqueueMessage()、next()、removeMessage()
2.1插入訊息
Handler的send()系列方法最終呼叫的就是enqueueMessage()
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) {//同步鎖,插入、刪除和獲取都是用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; } msg.markInUse(); msg.when = when;//隊的條件就這個when,意思距離處理的時間,數值越大排隊越靠後。 Message p = mMessages;//mMessages就是佇列的頭 boolean needWake; if (p == null || when == 0 || when < p.when) {//p==null說明佇列為空,when==0意思是現在就要處理,沒有任何延遲,when<p.when,說明比
這個方法的簡單描述就是:按照時間順序,將訊息插入訊息佇列中,有可能是頭,有可能是中間,也有可能是尾部。//現在的header還要早處理。 // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg;//換新header,新的header的next指向原來的header。 needWake = mBlocked; } else {//在連結串列中間或者尾部插入訊息。 // 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) {//p==null說明到了佇列的尾部 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; }
2.2獲取訊息
獲取訊息是在Looper的loop()迴圈中
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {//容錯? target不會為null
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
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;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
這個方法意思是說:取連結串列的header,如果這個訊息時間不滿足,執行緒就阻塞,等時間到了就取出這個訊息,然後第二個訊息作為header。也有可能新插入了一個訊息喚醒了執行緒,繼續迴圈判斷,直到取出一個訊息為止。2.3刪除訊息
removeMessages(Handler h, int what, Object object) 和removeMessages(Handler h, Runnable r, Object object) 這裡不做過多的解讀了;簡單概括還是對訊息佇列的遍歷,找到符合條件的Message,刪除這個Message,刪除的Message是要被回收的,回收的訊息放入訊息池中。3、訊息池
訊息池沒有對應的類,在SDK中以global pool稱呼。訊息池中的訊息資料結構是棧。
private static final Object sPoolSync = new Object();// 訊息池操作,同步鎖類
private static Message sPool;// 訊息池的棧頂
private static int sPoolSize = 0;//訊息池的訊息個數
private static final int MAX_POOL_SIZE = 50;//訊息池的訊息個數上限
上面是Message中屬性。
3.1提供訊息—出棧
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {//提供和回收 用的是同一個同步鎖
if (sPool != null) {//訊息池中 棧頂沒資料則訊息池為空
Message m = sPool;
sPool = m.next;//第一個訊息出棧,第二個訊息作為棧頂
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;//訊息池數量減少一個
return m;//
}
}
return new Message();//訊息池沒有訊息 則new一個。
}
3.2 訊息回收—壓棧
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
//清空訊息
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) {//執行緒同步
if (sPoolSize < MAX_POOL_SIZE) {//棧中訊息數量是有限制的
next = sPool;//壓棧,原來棧頂的作為本訊息的next,即第二個訊息
sPool = this;
sPoolSize++;
}
}
}
從上面兩個操作可以看出,MessageQueue中的訊息和MessagePool中的Message之間的連線的方式是一樣的,都是通過next指向下一個Message,但MessageQueue的資料結構是連結串列,但MessagePool是棧。