Message和MessageQueue
消息結構
每個消息用Message表示,Message主要包含以下內容:
filed | 含義 | 說明 |
---|---|---|
what | 消息類別 | 由用戶定義,用來區分不同的消息 |
arg1 | 參數1 | 是一種輕量級的傳遞數據的方式 |
arg2 | 參數2 | 是一種輕量級的傳遞數據的方式 |
obj | 消息內容 | 任意對象,但是使用Messenger跨進程傳遞Message時不能為null |
data | Bundle數據 | 比較複雜的數據建議使用該變量(相比上面幾個,這個縣的比較重量級) |
target | 消息響應方 | 關聯的Handler對象,處理Message時會調用它分發處理Message對象 |
when | 觸發響應時間 | 處理消息時間 |
next |
Message隊列裡的下一個Message對象 | 用next指向下一條Message,實現一個鏈表數據結構,用戶一般使用不到該字段。 |
這裡的用戶指一般的APP開發者。
一般不用手動設置target,調用Handler.obtainMessage()方法會自動的設置Message的target為當前的Handler。
得到Message之後可以調用sendToTarget(),發送消息給Handler,Handler再把消息放到message queue的尾部。
對Message除了給部分成員變量賦值外的操作都可以交由Handler來處理。
消息池
在通過Handler發送消息時,我們可以通過代碼Message message=new Message();
新建一條消息,但是我們並不推薦這樣做,因為這樣每次都會新建一條消息,很容易造成資源浪費。Android中設計了消息池用於避免該現象:
-
獲取消息
obtain()
從消息池中獲取消息:Message msg=Message.obtain();
obtain()方法源碼:public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; //從sPool中取出一個Message對象,並消息鏈表斷開 m.flags = 0; // clear in-use flag清除in-use flag sPoolSize--;//消息池的可用大小進行-1操作 return m; } } return new Message();// 當消息池為空時,直接創建Message對象 }
從消息池取Message,都是把消息池表頭的Message取走,再把表頭指向下一條消息
next
; -
回收消息
recycle()
把不再使用的消息回收到消息池mgs.recycle();
recycle()方法源碼:public void recycle() { if (isInUse()) {//判斷消息是否正在使用 if (gCheckRecycle) { throw new IllegalStateException("This message cannot be recycled because it " + "is still in use."); } return; } recycleUnchecked(); } /** * 對於不再使用的消息,加入到消息池 * 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. //將消息標示位置為IN_USE,並清空消息所有的參數。 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) {//當消息池沒有滿時,將Message對象加入消息池 next = sPool; sPool = this; sPoolSize++;//消息池的可用大小進行加1操作 } } }
消息回收,就是將Message內容重置後,再把Message加到鏈表的表頭,加入到消息池的過程;
MessageQueue
負責管理消息隊列,實際上Message類有一個next字段,會將Message對象串在一起成為一個消息隊列,所以並不需要LinkedList之類的數據結構將Message對象組在一起成為隊列。
-
創建消息隊列
在MessageQueue初始化的時候調用了nativeInit,這是一個Native方法:MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; mPtr = nativeInit();//通過native方法初始化消息隊列,其中mPtr是供native代碼使用 }
方法名由java層類的包名+類名+方法名組成,這不是標準,是習慣寫法,也可以採用其他名稱組合,具體是什麼名稱由JNINativeMethod方法中Java對象與c++對象的映射決定,此處是JNI方面的內容,不作過多解釋。static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) { NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); if (!nativeMessageQueue) { jniThrowRuntimeException(env, "Unable to allocate native queue"); return; } nativeMessageQueue->incStrong(env); android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue); } static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj, NativeMessageQueue* nativeMessageQueue) { env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr, reinterpret_cast<jint>(nativeMessageQueue)); }
在nativeInit中,new了一個Native層的MessageQueue的對象,並將其地址保存在了Java層MessageQueue的成員mPtr中,Android中有好多這樣的實現,一個類在Java層與Native層都有實現,通過JNI的GetFieldID與SetIntField把Native層的類的實例地址保存到Java層類的實例的mPtr成員中,比如Parcel。
再看NativeMessageQueue的實現:
NativeMessageQueue::NativeMessageQueue() : mInCallback(false), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
-
消息入隊
enqueueMessage()
enqueueMessage 用於將Message對象插入消息隊列。MessageQueue永遠是按照Message觸發的時間先後順序排列的,隊頭的消息是將要最早觸發的消息。當有消息需要加入消息隊列時,會從隊列頭開始遍歷,直到找到消息應該插入的合適位置,以保證所有消息的時間順序。
該方法會被Handler對象調用。
源碼如下:/** * 添加一條消息到消息隊列 * @param msg 要添加的消息 * @param when 消息處理時間 * @return 添加成功與否 */ boolean enqueueMessage(Message msg, long when) { if (msg.target == null) {// 每一個Message必須有一個target 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(); 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. //p為null(代表MessageQueue沒有消息) 或者msg的觸發時間是隊列中最早的, 則進入該該分支 msg.next = p; mMessages = msg; needWake = mBlocked; } else { //將消息按時間順序插入到MessageQueue。一般地,不需要喚醒事件隊列,除非 //消息隊頭存在barrier,並且同時Message是隊列中最早的異步消息。 // 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. //消息沒有退出,我們認為此時mPtr != 0 if (needWake) { nativeWake(mPtr); } } return true; }
-
消息輪詢
next()
最重要的方法,用於獲取下一個Message對象,如果沒有需要處理的Message對象,該方法將阻塞。MessageQueue用本地方法做同步互斥,因為這樣時間更精準。每個Message對象都有一個什麼時刻處理該Message對象的屬性when,沒到時間都不會處理該Message對象,如果時間不精準的話,會導致系統消息不能及時處理。/** * 依次從MessageQueue中取出Message * @return 消息 */ 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 循環迭代的首次為-1 int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } //阻塞操作,當等待nextPollTimeoutMillis時長,或者消息隊列被喚醒,都會返回。 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) { //查詢MessageQueue中的下一條異步消息 // 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();//設置消息flag成使用狀態 return msg;//成功地獲取MessageQueue中的下一條即將要執行的消息 } } else { // No more messages.//沒有消息了 nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) {//消息正在退出,返回null 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) { //沒有idle handlers 需要運行,則循環並等待。 // 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. //只有第一次循環時,會運行idle handlers,執行完成後,重置pendingIdleHandlerCount為0. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler//去掉handler的引用 boolean keep = false; try { keep = idler.queueIdle();//idle時執行的方法 } 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. //重置idle handler個數為0,以保證不會再次重複運行 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. //當調用一個空閒handler時,一個新message能夠被分發,因此無需等待可以直接查詢pending message. nextPollTimeoutMillis = 0; } }
nativePollOnce(ptr, nextPollTimeoutMillis)是一個native方法,是一個阻塞操作。其中nextPollTimeoutMillis代表下一個消息到來前,還需要等待的時長;當nextPollTimeoutMillis = -1時,表示消息隊列中無消息,會一直等待下去。空閒後,往往會執行IdleHandler中的方法。當nativePollOnce()返回後,next()從mMessages中提取一個消息。nativePollOnce()在native做了大量的工作,想深入研究可查看資料: Android消息機制2-Handler(Native層)。
-
移除消息
removeMessages()
就是將消息從鏈表移除,同時將移除的消息添加到消息池,提供循環複用。
採用了兩個while循環,第一個循環是從隊頭開始,移除符合條件的消息,第二個循環是從頭部移除完連續的滿足條件的消息之後,再從隊列後面繼續查詢是否有滿足條件的消息需要被移除。void removeMessages(Handler h, int what, Object object) { if (h == null) { return; } synchronized (this) { Message p = mMessages; //從消息隊列的頭部開始,移除所有符合條件的消息 // Remove all messages at front. while (p != null && p.target == h && p.what == what && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; p.recycleUnchecked(); p = n; } // Remove all messages after front. //移除剩餘的符合要求的消息 while (p != null) { Message n = p.next; if (n != null) { if (n.target == h && n.what == what && (object == null || n.obj == object)) { Message nn = n.next; n.recycleUnchecked(); p.next = nn; continue; } } p = n; } } }
-
退出消息隊列
消息退出的方式:- 當safe =true時,只移除尚未觸發的所有消息,對於正在觸發的消息並不移除;
- 當safe =flase時,移除所有的消息
void quit(boolean safe) { if (!mQuitAllowed) {// 當mQuitAllowed為false,表示不運行退出,強行調用quit()會拋出異常 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. //mQuitting=false,那麼認定為 mPtr != 0 nativeWake(mPtr); } }