詳解Android 消息處理機制
摘要
Android應用程序是通過消息來驅動的,當Android主線程啟動時就會在內部創建一個消息隊列。然后進入一個無限循環中,輪詢是否有新的消息需要處理。如果有新消息就處理新消息。如果沒有消息,就進入阻塞狀態,直到消息循環被喚醒。那么在Android系統中,消息處理機制是怎么實現的呢?在程序開發時,我們經常會使用Handler處理Message(消息)。所以可以知道Handler是個消息處理者,Message是消息主體。除此之外還有消息隊列和消息輪詢兩個角色。它們分別是MessageQueue和Looper,MessageQueue就是消息隊列,Looper負責輪詢消息。
簡介
我們已經知道Android的消息機制處理主要由Handler、Message、MessageQueue、Looper四個類的實現來完成。那么它們之間的關系是怎樣的?其中,Message是消息主體,它負責存儲消息的各種信息,包括發送消息的Handler對象、消息信息、消息標識等。MessageQueue就是消息隊列,在其內部以隊列的形式維護一組Message(消息)。Handler負責發送和處理消息。Looper負責輪詢消息隊列。
Android消息機制原理
創建線程消息隊列
在Android應用程序中,消息處理程序運行前首先要創建消息隊列(也就是MessageQueue)。在主線程中,通過調用Looper類的靜態成員函數prepareMainLooper()來創建消息隊列。在其他子線程中,通過調用靜態成員函數prepare()來創建。prepareMainLooper()與prepare()的實現:
/** * Initialize the current thread as a looper, marking it as an * application’s main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} * 用來初始化主線程中的Looper,有Android環境調用,不應該有用戶調用. */ public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) {throw new IllegalStateException('The main Looper has already been prepared.'); } sMainLooper = myLooper(); } } public static @Nullable Looper myLooper() { return sThreadLocal.get(); } /** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. * 交給用戶自己調用,通過loop()方法開啟消息循環.同時當不需要處理消息時,需要手動調用quit()方法退出循環. */ 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)); }
在這兩個函數調用的過程中,sThreadLocal變量都有被使用。這個變量是ThreadLocal類型的,用來保存當前線程中的Looper對象。也就是說在Android應用程序中每創建一個消息隊列,都有一個并且是唯一 一個與之對應的Looper對象。而且我們可以從源碼中看到當對象不唯一時就會拋出異常。
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); //創建消息隊列 mThread = Thread.currentThread(); }
從上面的源碼中可以看到,當Looper對象實例化的過程的同時會創建一個消息隊列。
消息循環過程
在消息隊列建立完成之后,調用Looper對象的靜態成員方法loop()就開始了消息循環。
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ 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(); // 在接收消息時有可能阻塞 if (msg == null) {//message為null時,退出消息循環return; } // This must be in a local variable, in case a UI event sets the logger final Printer logging = me.mLogging; if (logging != null) {...} final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {...} final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); final long end; try {msg.target.dispatchMessage(msg); //處理消息end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); } finally {if (traceTag != 0) { Trace.traceEnd(traceTag);} } if (slowDispatchThresholdMs > 0) {final long time = end - start;if (time > slowDispatchThresholdMs) {...} } if (logging != null) {...} // Make sure that during the course of dispatching the // identity of the thread wasn’t corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) {...} msg.recycleUnchecked(); } }
上面的源碼就是消息循環的過程,只用調用了loop()方法消息循環才開始起作用。當循環開始時:
獲取當前線程的Looper對象,如果為null,拋出異常; 獲取消息隊列,開始進入消息循環; 從消息隊列中獲取消息(調用MessageQueue的next()方法),如果為null,結束循環;否則,繼續執行; 處理消息,回收消息資源( msg.recycleUnchecked())。在消息循環過程中,通過MessageQueue的next()方法提供消息,在沒有信息時進入睡眠狀態,同時處理其他接口。這個過程至關重要,通過next()方法也決定了消息循環是否退出。
Message next() { final long ptr = mPtr; //與native方法相關,當mPtr為0時返回null,退出消息循環 if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; //0不進入睡眠,-1進入書面 for (;;) { if (nextPollTimeoutMillis != 0) {//處理當前線程中待處理的Binder進程間通信請求Binder.flushPendingCommands(); } //native方法,nextPollTimeoutMillis為-1時進入睡眠狀態 nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) {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. //非睡眠狀態下處理IdleHandler接口 for (int i = 0; i < pendingIdleHandlerCount; i++) {final IdleHandler idler = mPendingIdleHandlers[i];mPendingIdleHandlers[i] = null; // release the reference to the handlerboolean 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; } }
消息循環退出過程
從上面可以看到loop()方法是一個死循環,只有當MessageQueue的next()方法返回null時才會結束循環。那么MessageQueue的next()方法何時為null呢?在Looper類中我們看到了兩個結束的方法quit()和quitSalely()。兩者的區別就是quit()方法直接結束循環,處理掉MessageQueue中所有的消息,而quitSafely()在處理完消息隊列中的剩余的非延時消息(延時消息(延遲發送的消息)直接回收)時才退出。這兩個方法都調用了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); // 喚醒消息循環 } }
處理消息隊列中的消息:根據safe標志選擇不同的處理方式。
/** * API Level 1 * 處理掉消息隊列中所有的消息 */ private void removeAllMessagesLocked() { Message p = mMessages; while (p != null) { Message n = p.next; p.recycleUnchecked(); //回收消息資源 p = n; } mMessages = null; } /** * API Level 18 * 處理掉消息隊列中所有的延時消息 */ private void removeAllFutureMessagesLocked() { final long now = SystemClock.uptimeMillis(); Message p = mMessages; if (p != null) { if (p.when > now) {removeAllMessagesLocked(); } else {Message n;for (;;) { n = p.next; if (n == null) { return; } if (n.when > now) { //找出延時消息 break; } p = n;}p.next = null;//由于在消息隊列中按照消息when(執行時間排序,所以在第一個延時消息后的所有消息都是延時消息)do { p = n; n = p.next; p.recycleUnchecked(); //回收消息資源} while (n != null); } } }
消息發送過程
在Android應用程序中,通過Handler類向線程的消息隊列發送消息。在每個Handler對象中持有一個Looper對象和MessageQueue對象。
public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) {Log.w(TAG, 'The following Handler class should be static or leaks might occur: ' + klass.getCanonicalName()); } } mLooper = Looper.myLooper(); //獲取Looper對象 if (mLooper == null) {...} mQueue = mLooper.mQueue; //獲取消息隊列 mCallback = callback; mAsynchronous = async; }
在Handler類中,我們可以看到多種sendMessage方法,而它們最終都調用了同一個方法sendMessageAtTime()方法。
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); }
可以看出這兩個方法十分容易理解,就是通過MessageQueue對象調用enqueueMessage()方法向消息隊列中添加消息。
boolean enqueueMessage(Message msg, long when) { // Handler為null 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.nextprev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) {nativeWake(mPtr); //喚醒消息循環 } } return true; }
從源碼可以看出,一個消息插入到消息隊列中需要以下步驟:
消息持有的Handler對象為null,拋出異常;當消息已經被消費,拋出異常;當消息隊列沒有消息時,直接插入;當消息隊列存在消息時,通過比較消息的執行時間,將消息插入到相應的位置;判斷是否需要喚醒消息循環。
消息處理過程
在消息循環過程中,如果有新的消息加入,就開始處理消息。從上面的分析中,我們可以看到在消息循環中,目標消息會調用其Handler對象的dispatchMessage()方法,這個就是處理消息的方法。
/** * Handle system messages here. */ public void dispatchMessage(Message msg) { // 消息Callback接口不為null,執行Callback接口 if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) {//Handler Callback接口不為null,執行接口方法if (mCallback.handleMessage(msg)) { return;} } handleMessage(msg); //處理消息 } }
從源碼可以看出,Handler處理消息分為3中情況。
當Message中的callback不為null時,執行Message中的callback中的方法。這個callback時一個Runnable接口。 當Handler中的Callback接口不為null時,執行Callback接口中的方法。 直接執行Handler中的handleMessage()方法。當Looper開始調用loop()時主線程為什么不會卡死
在進行完上面的分析后,我們都知道Looper.loop()進入到了一個死循環,那么在主線程中執行這個死循環為什么沒有造成主線程卡死或者說在主線程中的其他操作還可以順利的進行,比如說UI操作。因為Android應用程序是通過消息驅動的,所以Android應用程序的操作也是通過Android的消息機制來實現的。這個時候就需要分析一下Android程序啟動的入口類ActivityThread。我們都知道當Android程序啟動時在Java層就是以ActivityThread的main()方法為入口的,這也就是我們所說的主線程。
public static void main(String[] args) { ... ... ... Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); //建立Binder通道 (創建新線程),與ActivityManagerService建立鏈接 if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } ... ... ... Looper.loop(); throw new RuntimeException('Main thread loop unexpectedly exited'); }
從ActivityThread的main()方法中我們可以看到Looper的初始化以及消息循環的開始,同時還有一個關鍵的方法attach()與ActivityManagerService建立鏈接,這里建立鏈接是為了之后相應Activity中各種事件的發生。講到這里還涉及到Native層Looper的初始化,在Looper初始化時會建立一個管道來維護消息隊列的讀寫并通過epoll機制監聽讀寫事件(一種IO多路復用機制)。
當沒有新消息需要處理時,主線程就會阻塞在管道上,直到有新的消息需要處理; 當其他線程有消息發送到消息隊列時會通過管道來寫數據;在我們調試程序時,我們通過函數的調用棧就可以發現其中的道理:
這也印證了開始的那句話——Android應用程序是通過消息來驅動的。
是否所有的消息都會在指定時間開始執行
這個問題的意思是當我們像消息隊列中發送消息時(比如延時1000ms執行一個消息postDelay(action, 1000)),是不是會在1000ms后去執行這個消息。答案是不一定。我們只Android的消息是按照時間順序保存在消息隊列中的,如果我們向隊列中添加多個消息,比如10000個延時1000ms執行的消息,那么其實最后一個執行的消息和第一個執行的消息的執行時間是不一樣的。
總結
至此Android系統的消息處理機制就分析完畢了。在Android應用程序中消息處理主要分為3個過程:
啟動Looper中的消息循環,開始監聽消息隊列。 通過Handler發送消息到消息隊列。 通過消息循環調用Handler對象處理新加入的消息。在使用消息隊列時,主線程中在程序啟動時就會創建消息隊列,所以我們使用主線程中的消息機制時,不需要初始化消息循環和消息隊列。在子線程中我們需要初始化消息隊列,并且注意在不需要使用消息隊列時,應該及時調用Looper的quit或者quitSafely方法關閉消息循環,否則子線程可能一直處于等待狀態。
以上就是詳解Android 消息處理機制的詳細內容,更多關于Android 消息處理機制的資料請關注好吧啦網其它相關文章!
相關文章: