android handler (2)
阿新 • • 發佈:2020-08-27
享元模式
主要用於減少建立物件的數量,以減少記憶體佔用和提高效能。
private static Message sPool; private static int sPoolSize = 0; private static final int MAX_POOL_SIZE = 50; 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(); }
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 = UID_NONE; workSourceUid = UID_NONE; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }
同步屏障
messagequeue佇列是沒有限制大小的,意味著可以無限往裡面傳送訊息(超過記憶體就會oom),我們知道系統重新整理螢幕ui也是通過handler 往主執行緒的messagequeue佇列裡面傳送訊息,來重新整理ui,那麼怎麼保證能夠及時的重新整理ui?這裡就需要通過同步屏障來實現。
首先我們看一下messagequeue裡面的next()方法:
next(){ for (;;) { 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) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } } }
這行程式碼:msg.target == null
,我們在傳送訊息的時候,會給msg.target 持有傳送該訊息的hander,那麼什麼時候target會為空?
繼續看原始碼,在messagequeue裡面有一個postSyncBarrier
方法:
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
可以看到這邊並沒有給target的賦值。這個方法就是傳送一個同步屏障到佇列中。回到上面的
next方法:
next(){
for (;;) {
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) {
// 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;
}
}
}
當訊息佇列遇到同步屏障之後,會迴圈整個佇列,並把非同步訊息取出來優先處理,這樣就能保證佇列不被太多的訊息卡死,避免系統訊息沒辦法執行。