Android消息機制是Android操做系統中比較重要的一塊。具體使用方法在這裏再也不闡述,能夠參考Android的官方開發文檔。java
消息機制的主要用途有兩方面:編程
一、線程之間的通訊。好比在子線程中想更新UI,就經過發送更新消息到UI線程中來實現。緩存
二、任務延遲執行。好比30秒後執行刷新任務等。安全
消息機制運行的大概示意圖以下:app
一個線程中只能有一個Looper對象,一個Looper對象中持有一個消息隊列,一個消息隊列中維護多個消息對象,用一個Looper能夠建立多個Handler對象,Handler對象用來發送消息到消息隊列中、和處理Looper分發給本身的消息(也就是本身以前發送的那些消息),這些Handler對象能夠跨線程運行,可是最終消息的處理,都是在建立該Handler的線程中運行。 less
整個消息機制的一個重要用途,就是線程間通訊,並且大部分都是工做線程向主線程發送數據和消息。那麼爲何Android系統要這樣設計呢?其實典型的Android應用,都是事件驅動的GUI程序,跟Window的GUI程序很相似。這種程序,特色就是基於事件運行,好比點擊事件或者滑動事件。因此這種狀況下,確定是要有一個專門的線程來負責事件的監聽和分發。在Android中,系統默認啓動的主線程,就幹這個事情了。異步
因爲該消息分發線程有着本身獨特的任務,因此若是該線程阻塞了的話,系統就會出現無響應的狀況。這樣,天然就不可能把耗時的任務放在該線程中,因此官方推薦是把耗時的任務放到工做線程中執行。可是不少時候,耗時任務的執行結果,都是要反饋到UI上的。而Android中的View,是不能在非UI線程中更新的,由於View不是線程安全的,沒有同步,因此必需要把數據經過線程間通訊的模式,發送到UI線程,這能才能夠正常更新UI。async
因此你們會問,Android爲何不把View設計成線程安全的呢?那麼在Java這種指令式編程語言中,線程安全就是意味着要加鎖。其實咱們能夠思考一下,整個View響應事件,事件從屏幕產生,通過Framework,最後到kernel,而後kernel處理完成後,向上傳遞到framework,而後又傳遞到View層。以下圖所示:編程語言
那麼若是整個流程都是線程安全的話,就會面臨着兩個相對的加鎖流程,這種反方向的加鎖,很容易就會致使死鎖的狀況發生。這是絕大多數GUI系統存在的問題,因此絕大多數GUI系統都是採用事件分發機制來實現的。因此說Android爲何設計成單線程模型了。ide
在熟悉了基本用法以後,有必要深刻探索一下。
Android消息機制的framework層主要圍繞Handler、Looper、Message、MessageQueue這四個對象來操做。消息機制主要是對消息進行生成、發送、存儲、分發、處理等操做。
該類表明的是消息機制中的消息對象。是在消息機制中被建立,用來傳遞數據以及操做的對象,也負責維護消息對象緩存池。
Message對象中主要有如下幾個屬性:
what:消息類型。
arg一、arg二、obj、data:該消息的數據域
when:該消息應該被處理的時間,該字段的值爲SystemClock.uptimeMillis()。當該字段值爲0時,說明該消息須要被放置到消息隊列的首部。
target:發送和處理該消息的Handler對象。
next:對象池中該消息的下一個消息。
Message對象中,主要維護了一個Message對象池,由於系統中會頻繁的使用到Message對象,因此用對象池的方式來減小頻繁建立對象帶來的開支。Message對象池使用單鏈表實現。最大數量限制爲50。因此官方推薦咱們經過對象池來獲取Message對象。
特別注意的是,咱們日常使用的都是普通的Message對象,也就是同步的Message對象。其實還有兩種特殊的Message對象,目前不多被使用到,但也有必要了解一下。
第一個是同步的障礙消息(Barrier Message),該消息的做用就是,若是該消息到達消息隊列的首部,則消息隊列中其餘的同步消息就會被阻塞,不能被處理。障礙消息的特徵是target==null&&arg1==barrierToken
第二個是異步消息,異步消息不會被上面所說的障礙消息影響。經過Message對象的setAsynchronous(boolean async)方法來設置一個消息爲異步消息。
該類表明的是消息機制中的消息隊列。它主要就是維護一個線程安全的消息隊列,提供消息的入隊、刪除、以及阻塞方式的輪詢取出等操做。
該類表明的是消息機制中的消息分發器。 有了消息,有了消息隊列,還缺乏處理消息分發機制的對象,Looper就是處理消息分發機制的對象。它會把每一個Message發送到正確的處理對象上進行處理。若是一個Looper開始工做後,一直沒有消息處理的話,那麼該線程就會被阻塞。在非UI線程中,這時候應該監聽當前MessageQueue的Idle事件,若是當前有Idle事件,則應該退出當前的消息循環,而後結束該線程,釋放相應的資源。
該類表明的是消息機制中的消息發送和處理器。有了消息、消息隊列、消息分發機制,還缺乏的就是消息投遞和消息處理。Handler就是用來作消息投遞和消息處理的。Handler事件處理機制採用一種按自由度從高到低的優先級進行消息的處理,正常狀況下,一個Handler對象能夠設置一個callback屬性,一個Handler對象能夠操做多個Message對象,從某種程度上來講,建立一個Message對象比給一個Handler對象設置callback屬性來的自由,而給一個Handler對象設置callback屬性比衍生一個Handler子類來的自由,因此消息處理優先級爲Message>Handler.callback.Handler.handleMessage()。
重要部分的源代碼解析,源代碼基於sdk 23。
普通的消息對象,包含了消息類型、數據、行爲。內部包含了一個用單鏈表實現的對象池,最大數量爲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(); }
僞代碼:
public static Message obtain() { synchronized (sPoolSync) { if (對象池不爲空) { 從單鏈表實現的對象池中取出一個對象(從鏈表頭獲取); 清空該對象標誌位(在使用中、異步等); 修正對象池大小; return 取出的消息對象; } } return 新建消息對象; }
源代碼:
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; sPool = this; sPoolSize++; } } }
僞代碼:
void recycleUnchecked() { 標誌爲正在使用中; 清空當前對象的其餘數據; synchronized (sPoolSync) { if (對象池沒容量有達到上限) { 在單鏈表表頭插入該對象; 修正對象池大小; } } }
使用ThreadLocal來實現線程做用域的控制,每一個線程最多有一個Looper對象,內部持有一個MessageQueue的引用。
源代碼:
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)); }
僞代碼:
private static void prepare(boolean quitAllowed) { if (當前線程中已經有了一個Looper對象) { throw new RuntimeException("一個線程只能建立一個Looper對象"); } 從新實例化一個能夠退出的Looper; 把該Looper對象和當前線程關聯起來; }
源代碼:
public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); } }
僞代碼:
public static void loop() { 獲取當前線程的Looper對象; if (當前線程沒有Looper對象) { throw new RuntimeException("沒有Looper; Looper.prepare() 沒有在當前線程被調用過"); } 獲取該Looper對象關聯的MessageQueue; 清除IPC身份標誌; for (;;) { 從MessageQueue中獲取一個Message,若是當前MessageQueue沒有消息,就會阻塞; if (沒有取到消息) { // 沒有消息意味着消息隊列退出了. return; } 打印日誌; 調用當前Message對象的target來處理消息,也就是發送該Message的Handler對象; 打印日誌; 獲取新的IPC身份標識; if (IPC身份標識改變了) { 打印警告信息; 回收該消息,放入到Message對象池中; } }
負責消息的發送、定時發送、延遲發送、消息處理等動做。
源代碼:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
僞代碼:
public void dispatchMessage(Message msg) { if (該消息的callback屬性不爲空) { 運行該消息的callback對象的run()方法; } else { if (當前Handler對象的mCallback屬性不爲空) { if (mCallback對象成功處理了消息) { return; } } Handler內部處理該消息; } }
使用單鏈表的方式維護一個消息隊列,提升頻繁插入刪除消息等操做的性能,該鏈表用消息的when字段進行排序,先被處理的消息排在鏈表前部。內部的阻塞輪詢和喚醒等操做,使用JNI來實現。
源代碼:
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) { 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. msg.next = p; mMessages = msg; 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) { 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; }
僞代碼:
boolean enqueueMessage(Message msg, long when) { if (該消息沒有target) { throw new IllegalArgumentException("Message對象必需要有一個target"); } if (該消息正在被使用) { throw new IllegalStateException(msg + " 該消息正在使用中"); } synchronized (this) { if (消息隊列退出了) { 打印警告信息; 回收該消息,返回到Message對象池; return false; } 設置該消息爲正在使用中; 設置消息將要被處理的時間; Message p = mMessages; boolean needWake; if (msg隊列爲空 || 該消息對象請求放到隊首 || 執行時間先於當前隊首msg的執行時間(當前隊列中全是delay msg)) { 把當前msg添加到msg隊列首部; 若是阻塞了,設置爲須要被喚醒; } else { if (阻塞了 && 隊首是barrier && 當前msg是異步msg) { 設置爲須要被喚醒 } for (;;) { 根據msg.when的前後,找到合適的插入位置,先執行的在隊列前面; if (須要喚醒 && 插入位置以前有異步消息) { 不須要喚醒; } } 插入到合適的位置; } if (須要喚醒) { 調用native方法進行本地喚醒; } } return true; }
源代碼:
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) { // 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; } }
僞代碼:
Message next() { if (消息隊列退出了) { return null; } 把Idle事件的次數標記爲第一次; 下一次輪詢的等待(阻塞)時間設爲0; for (;;) { if (下一次輪詢須要阻塞) { 清楚Binder的pending command,用來釋放資源; } 使用當前的設置輪詢阻塞時間去作一次native的輪詢,若是阻塞時間大於0,則會阻塞,直到取到消息爲止; synchronized (this) { if (消息隊列首部爲barrier消息) { 取出第一個異步消息; } if (查詢到知足條件的消息) { if (還沒到該消息的執行時間) { 設置下一次輪詢的阻塞時間爲msg.when - now,最大不超過Integer.MAX_VALUE; } else { 阻塞標識設置爲false; 取出該消息,重定向鏈表頭; 標記該消息爲在使用中; return 該消息; } } else { 沒有消息,設置下一次輪詢阻塞時間爲-1,不阻塞; } if (消息隊列退出了) { 釋放資源; 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 (第一次Idle事件) { 計算Idle監聽器數量; } if (沒有Idle監聽器) { 阻塞標識設置爲true; continue; } 生成Idle監聽對象; } for (int i = 0; i < pendingIdleHandlerCount; i++) { 通知Idle事件的監聽對象,根據標識來肯定這些監聽器是否繼續監聽。 } 設置Idle事件的標識爲不是第一次; 調用了Idle監聽器以後,可能有新的消息進入隊列,因此下一次輪詢阻塞時間設置爲0; } }