Handler原始碼簡析
訊息------Message
-
訊息的複用:
Message.obtain()
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(); }
- 從Message 連結串列的頭節點-----sPool中獲取第一個訊息,然後更新頭節點
- 清空next資訊
- 重置in-use標誌位
- 更新訊息池size
-
訊息的回收:
Message.recyclerUnchecked()
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) {//MAX_POOL_SIZE==50 next = sPool; sPool = this; sPoolSize++; } } }
- 標識訊息used;
- 重置引數
- 加入到訊息池
訊息佇列-------MessageQueue
關鍵:使用連結串列儲存MessageQueue;使用一個阻塞標記標示是否列表是否為空;記錄next barrier的token
..... Message mMessages;//連結串列頭 // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout. private boolean mBlocked; //阻塞標示 // The next barrier token. // Barriers are indicated by messages with a null target whose arg1 field carries the token. private int mNextBarrierToken; //分隔欄tekon .....
>barrier是一個target為null的Message,使用postSyncBarrier(SystemClock.uptimeMillis())方法加入MessageQueue中,它的arg1引數儲存著token。 > >當從MessageQueue中取出訊息時,遇到barrier會停止取出barrier後面的同步訊息,稱為“同步訊息分隔欄”。
-
訊息入隊:
enqueueMessage()
boolean enqueueMessage(Message msg, long when) { if (msg.target == null) {//訊息必須有一個處理它的目標Handler throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) {//檢查"使用中"標記 throw new IllegalStateException(msg + " This message is already in use."); } 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(); return false; } msg.markInUse();//訊息入隊後標記為--used msg.when = when; Message p = mMessages; boolean needWake; //新增訊息到連結串列中 if (p == null || when == 0 || when < p.when) { //之前是空連結串列的時候讀取訊息會阻塞,新新增訊息後喚醒 msg.next = p; mMessages = msg; needWake = mBlocked; } else { //插入訊息到佇列時,若訊息佇列阻塞、佇列頭是barrier並且當前訊息是非同步的時,需要喚醒佇列 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; }
-
訊息出隊:
MessageQueue.next()
Message next() { //如果訊息的 looper 退出,就退出這個方法 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) { //獲取下一個訊息 final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages;//當前的連結串列頭 if (msg != null && msg.target == null) { //如果訊息沒有target---是barrier,向後遍歷找到第一個非同步的訊息 do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) {//如果這個訊息還沒到處理時間,就設定個時間過段時間再處理 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // 訊息可以立即處理 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 { // 訊息連結串列裡沒有訊息了 nextPollTimeoutMillis = -1; } //如果收到退出的訊息,並且所有等待處理的訊息都處理完時,呼叫 Native 方法銷燬佇列 if (mQuitting) { dispose(); return null; } if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } 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; } }
mIdleHandlers:監聽MessageQueue阻塞的介面List,MessageQueue在阻塞時會遍歷並呼叫idler.queueIdle()方法,該方法返回false則會在一次呼叫後移除該idler。
訊息排程器----Looper
屬性:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static Looper sMainLooper;//主執行緒的 Looepr final MessageQueue mQueue;//與之管理的訊息佇列 final Thread mThread;//所在的執行緒 private Printer mLogging; private long mTraceTag; /* If set, the looper will show a warning log if a message dispatch takes longer than time. */ private long mSlowDispatchThresholdMs;
-
Looper初始化
Looper.prepare()
:
使用ThreadLocal保證每個執行緒各自擁有自己一個的Looper。
public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
-
迴圈處理訊息:
Looper.loop()
public static void loop() { final Looper me = myLooper(); if (me == null) {//當前執行緒必須建立 Looper 才可以執行 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) {//無限迴圈 Message msg = queue.next(); //從訊息佇列中讀取訊息,可能會阻塞 if (msg == null) {//當訊息佇列中沒有訊息時就會返回,不過這隻發生在 queue 退出的時候 return; } //... try { msg.target.dispatchMessage(msg);//呼叫訊息關聯的 Handler 處理訊息 } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } //... msg.recycleUnchecked();//標記這個訊息被回收 } }
- 無限迴圈取出Message,呼叫對應Handler的dispatchMessage()方法處理訊息
- 標記訊息被回收
-
Looper.quit()方法退出迴圈,其呼叫的是MessageQueue.quit()方法。
void quit(boolean safe) { if (!mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit."); } synchronized (this) { if (mQuitting) { return; } mQuitting = true; if (safe) { removeAllFutureMessagesLocked(); } else { removeAllMessagesLocked(); } // We can assume mPtr != 0 because mQuitting was previously false. nativeWake(mPtr); } }
-
判斷並更新標識位;
-
removeAllMessagesLocked();
立即把訊息連結串列中的所有訊息全部回收
-
- 在停止後如果 Handler 還發送訊息,會返回 false,表示入隊失敗 - 這個方法是不安全的,一般建議使用下面那個
- removeAllFutureMessagesLocked();
- 只會將還未執行的訊息回收掉 - 在呼叫之後新增進入的訊息不會被處理,Handler.sendMessage 也會返回 false
Handler
- 主要屬性:
final Looper mLooper; final MessageQueue mQueue; final Callback mCallback;//構造Handler時可傳的自定義處理方法 final boolean mAsynchronous; IMessenger mMessenger;`
- 訊息處理方法:dispatchMessage(Message msg)
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
-
如果訊息定義了callback,則呼叫handleCallback(msg),該方法是呼叫Message自身的callback方法;
private static void handleCallback(Message message) { message.callback.run(); }
-
訊息的callback為null,呼叫mCallback的handleMessage(msg)方法,如果mCallback.handleMessage(msg)返回true,直接返回,不再呼叫handleMessage(msg);
mCallback是構造Handler時可傳的自定義處理方法,其內部定義了一個handleMessage(Message msg)方法;
-
最後呼叫handleMessage(msg)方法。
-
傳送訊息
- Message-----sendMessage(Message msg)
- Runnable------post(Runnable r)
post方法會將Runnable包裝為Message,然後呼叫sendMessageDelayed()方法;
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
sendMessage(Message msg)最後還是呼叫了訊息佇列的
enqueueMessage()
方法:public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0); } public final boolean sendMessageDelayed(Message msg, long delayMillis){ if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } public boolean sendMessageAtTime(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); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
-
移除訊息,呼叫mQueue.removeMessages方法,從佇列中移除訊息。
public final void removeCallbacks(Runnable r){ mQueue.removeMessages(this, r, null); } public final void removeMessages(int what) { mQueue.removeMessages(this, what, null); }
引數r與Message.callback 比較