詳解Android 消息處理機(jī)制
摘要
Android應(yīng)用程序是通過(guò)消息來(lái)驅(qū)動(dòng)的,當(dāng)Android主線程啟動(dòng)時(shí)就會(huì)在內(nèi)部創(chuàng)建一個(gè)消息隊(duì)列。然后進(jìn)入一個(gè)無(wú)限循環(huán)中,輪詢是否有新的消息需要處理。如果有新消息就處理新消息。如果沒(méi)有消息,就進(jìn)入阻塞狀態(tài),直到消息循環(huán)被喚醒。
那么在Android系統(tǒng)中,消息處理機(jī)制是怎么實(shí)現(xiàn)的呢?在程序開(kāi)發(fā)時(shí),我們經(jīng)常會(huì)使用Handler處理Message(消息)。所以可以知道Handler是個(gè)消息處理者,Message是消息主體。除此之外還有消息隊(duì)列和消息輪詢兩個(gè)角色。它們分別是MessageQueue和Looper,MessageQueue就是消息隊(duì)列,Looper負(fù)責(zé)輪詢消息。
簡(jiǎn)介
我們已經(jīng)知道Android的消息機(jī)制處理主要由Handler、Message、MessageQueue、Looper四個(gè)類(lèi)的實(shí)現(xiàn)來(lái)完成。那么它們之間的關(guān)系是怎樣的?
其中,Message是消息主體,它負(fù)責(zé)存儲(chǔ)消息的各種信息,包括發(fā)送消息的Handler對(duì)象、消息信息、消息標(biāo)識(shí)等。MessageQueue就是消息隊(duì)列,在其內(nèi)部以隊(duì)列的形式維護(hù)一組Message(消息)。Handler負(fù)責(zé)發(fā)送和處理消息。Looper負(fù)責(zé)輪詢消息隊(duì)列。
Android消息機(jī)制原理
創(chuàng)建線程消息隊(duì)列
在Android應(yīng)用程序中,消息處理程序運(yùn)行前首先要?jiǎng)?chuàng)建消息隊(duì)列(也就是MessageQueue)。在主線程中,通過(guò)調(diào)用Looper類(lèi)的靜態(tài)成員函數(shù)prepareMainLooper()來(lái)創(chuàng)建消息隊(duì)列。在其他子線程中,通過(guò)調(diào)用靜態(tài)成員函數(shù)prepare()來(lái)創(chuàng)建。
prepareMainLooper()與prepare()的實(shí)現(xiàn):
/** * 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()} * 用來(lái)初始化主線程中的Looper,有Android環(huán)境調(diào)用,不應(yīng)該有用戶調(diào)用. */ 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()}. * 交給用戶自己調(diào)用,通過(guò)loop()方法開(kāi)啟消息循環(huán).同時(shí)當(dāng)不需要處理消息時(shí),需要手動(dòng)調(diào)用quit()方法退出循環(huán). */ 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)); }
在這兩個(gè)函數(shù)調(diào)用的過(guò)程中,sThreadLocal變量都有被使用。這個(gè)變量是ThreadLocal類(lèi)型的,用來(lái)保存當(dāng)前線程中的Looper對(duì)象。也就是說(shuō)在Android應(yīng)用程序中每創(chuàng)建一個(gè)消息隊(duì)列,都有一個(gè)并且是唯一 一個(gè)與之對(duì)應(yīng)的Looper對(duì)象。而且我們可以從源碼中看到當(dāng)對(duì)象不唯一時(shí)就會(huì)拋出異常。
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); //創(chuàng)建消息隊(duì)列 mThread = Thread.currentThread(); }
從上面的源碼中可以看到,當(dāng)Looper對(duì)象實(shí)例化的過(guò)程的同時(shí)會(huì)創(chuàng)建一個(gè)消息隊(duì)列。
消息循環(huán)過(guò)程
在消息隊(duì)列建立完成之后,調(diào)用Looper對(duì)象的靜態(tài)成員方法loop()就開(kāi)始了消息循環(huán)。
/** * 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 (;;) { //開(kāi)始消息循環(huán) Message msg = queue.next(); // 在接收消息時(shí)有可能阻塞 if (msg == null) { //message為null時(shí),退出消息循環(huán) 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(); } }
上面的源碼就是消息循環(huán)的過(guò)程,只用調(diào)用了loop()方法消息循環(huán)才開(kāi)始起作用。當(dāng)循環(huán)開(kāi)始時(shí):
- 獲取當(dāng)前線程的Looper對(duì)象,如果為null,拋出異常;
- 獲取消息隊(duì)列,開(kāi)始進(jìn)入消息循環(huán);
- 從消息隊(duì)列中獲取消息(調(diào)用MessageQueue的next()方法),如果為null,結(jié)束循環(huán);否則,繼續(xù)執(zhí)行;
- 處理消息,回收消息資源( msg.recycleUnchecked())。
在消息循環(huán)過(guò)程中,通過(guò)MessageQueue的next()方法提供消息,在沒(méi)有信息時(shí)進(jìn)入睡眠狀態(tài),同時(shí)處理其他接口。這個(gè)過(guò)程至關(guān)重要,通過(guò)next()方法也決定了消息循環(huán)是否退出。
Message next() { final long ptr = mPtr; //與native方法相關(guān),當(dāng)mPtr為0時(shí)返回null,退出消息循環(huán) if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; //0不進(jìn)入睡眠,-1進(jìn)入書(shū)面 for (;;) { if (nextPollTimeoutMillis != 0) { //處理當(dāng)前線程中待處理的Binder進(jìn)程間通信請(qǐng)求 Binder.flushPendingCommands(); } //native方法,nextPollTimeoutMillis為-1時(shí)進(jìn)入睡眠狀態(tài) 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; //更新到睡眠狀態(tài) } // Process the quit message now that all pending messages have been handled. //消息循環(huán)退出 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. //非睡眠狀態(tài)下處理IdleHandler接口 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; } }
消息循環(huán)退出過(guò)程
從上面可以看到loop()方法是一個(gè)死循環(huán),只有當(dāng)MessageQueue的next()方法返回null時(shí)才會(huì)結(jié)束循環(huán)。那么MessageQueue的next()方法何時(shí)為null呢?
在Looper類(lèi)中我們看到了兩個(gè)結(jié)束的方法quit()和quitSalely()。兩者的區(qū)別就是quit()方法直接結(jié)束循環(huán),處理掉MessageQueue中所有的消息,而quitSafely()在處理完消息隊(duì)列中的剩余的非延時(shí)消息(延時(shí)消息(延遲發(fā)送的消息)直接回收)時(shí)才退出。這兩個(gè)方法都調(diào)用了MessageQueue的quit()方法。
void quit(boolean safe) { if (!mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit."); } synchronized (this) { if (mQuitting) { return; } mQuitting = true; //設(shè)置退出狀態(tài) //處理消息隊(duì)列中的消息 if (safe) { removeAllFutureMessagesLocked(); //處理掉所有延時(shí)消息 } else { removeAllMessagesLocked(); //處理掉所有消息 } // We can assume mPtr != 0 because mQuitting was previously false. nativeWake(mPtr); // 喚醒消息循環(huán) } }
處理消息隊(duì)列中的消息:根據(jù)safe標(biāo)志選擇不同的處理方式。
/** * API Level 1 * 處理掉消息隊(duì)列中所有的消息 */ private void removeAllMessagesLocked() { Message p = mMessages; while (p != null) { Message n = p.next; p.recycleUnchecked(); //回收消息資源 p = n; } mMessages = null; } /** * API Level 18 * 處理掉消息隊(duì)列中所有的延時(shí)消息 */ 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) { //找出延時(shí)消息 break; } p = n; } p.next = null; //由于在消息隊(duì)列中按照消息when(執(zhí)行時(shí)間排序,所以在第一個(gè)延時(shí)消息后的所有消息都是延時(shí)消息) do { p = n; n = p.next; p.recycleUnchecked(); //回收消息資源 } while (n != null); } } }
消息發(fā)送過(guò)程
在Android應(yīng)用程序中,通過(guò)Handler類(lèi)向線程的消息隊(duì)列發(fā)送消息。在每個(gè)Handler對(duì)象中持有一個(gè)Looper對(duì)象和MessageQueue對(duì)象。
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對(duì)象 if (mLooper == null) {...} mQueue = mLooper.mQueue; //獲取消息隊(duì)列 mCallback = callback; mAsynchronous = async; }
在Handler類(lèi)中,我們可以看到多種sendMessage方法,而它們最終都調(diào)用了同一個(gè)方法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; } //向消息隊(duì)列中添加消息 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); }
可以看出這兩個(gè)方法十分容易理解,就是通過(guò)MessageQueue對(duì)象調(diào)用enqueueMessage()方法向消息隊(duì)列中添加消息。
boolean enqueueMessage(Message msg, long when) { // Handler為null if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } //消息已經(jīng)被消費(fèi) 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. // 隊(duì)列沒(méi)有消息,直接加入 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; // 根據(jù)執(zhí)行時(shí)間插入到相應(yīng)位置 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); //喚醒消息循環(huán) } } return true; }
從源碼可以看出,一個(gè)消息插入到消息隊(duì)列中需要以下步驟:
消息持有的Handler對(duì)象為null,拋出異常;當(dāng)消息已經(jīng)被消費(fèi),拋出異常;
當(dāng)消息隊(duì)列沒(méi)有消息時(shí),直接插入;
當(dāng)消息隊(duì)列存在消息時(shí),通過(guò)比較消息的執(zhí)行時(shí)間,將消息插入到相應(yīng)的位置;
判斷是否需要喚醒消息循環(huán)。
消息處理過(guò)程
在消息循環(huán)過(guò)程中,如果有新的消息加入,就開(kāi)始處理消息。從上面的分析中,我們可以看到在消息循環(huán)中,目標(biāo)消息會(huì)調(diào)用其Handler對(duì)象的dispatchMessage()方法,這個(gè)就是處理消息的方法。
/** * Handle system messages here. */ public void dispatchMessage(Message msg) { // 消息Callback接口不為null,執(zhí)行Callback接口 if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { //Handler Callback接口不為null,執(zhí)行接口方法 if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); //處理消息 } }
從源碼可以看出,Handler處理消息分為3中情況。
- 當(dāng)Message中的callback不為null時(shí),執(zhí)行Message中的callback中的方法。這個(gè)callback時(shí)一個(gè)Runnable接口。
- 當(dāng)Handler中的Callback接口不為null時(shí),執(zhí)行Callback接口中的方法。
- 直接執(zhí)行Handler中的handleMessage()方法。
當(dāng)Looper開(kāi)始調(diào)用loop()時(shí)主線程為什么不會(huì)卡死
在進(jìn)行完上面的分析后,我們都知道Looper.loop()進(jìn)入到了一個(gè)死循環(huán),那么在主線程中執(zhí)行這個(gè)死循環(huán)為什么沒(méi)有造成主線程卡死或者說(shuō)在主線程中的其他操作還可以順利的進(jìn)行,比如說(shuō)UI操作。因?yàn)锳ndroid應(yīng)用程序是通過(guò)消息驅(qū)動(dòng)的,所以Android應(yīng)用程序的操作也是通過(guò)Android的消息機(jī)制來(lái)實(shí)現(xiàn)的。這個(gè)時(shí)候就需要分析一下Android程序啟動(dòng)的入口類(lèi)ActivityThread。我們都知道當(dāng)Android程序啟動(dòng)時(shí)在Java層就是以ActivityThread的main()方法為入口的,這也就是我們所說(shuō)的主線程。
public static void main(String[] args) { ... ... ... Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); //建立Binder通道 (創(chuàng)建新線程),與ActivityManagerService建立鏈接 if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } ... ... ... Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
從ActivityThread的main()方法中我們可以看到Looper的初始化以及消息循環(huán)的開(kāi)始,同時(shí)還有一個(gè)關(guān)鍵的方法attach()與ActivityManagerService建立鏈接,這里建立鏈接是為了之后相應(yīng)Activity中各種事件的發(fā)生。講到這里還涉及到Native層Looper的初始化,在Looper初始化時(shí)會(huì)建立一個(gè)管道來(lái)維護(hù)消息隊(duì)列的讀寫(xiě)并通過(guò)epoll機(jī)制監(jiān)聽(tīng)讀寫(xiě)事件(一種IO多路復(fù)用機(jī)制)。
- 當(dāng)沒(méi)有新消息需要處理時(shí),主線程就會(huì)阻塞在管道上,直到有新的消息需要處理;
- 當(dāng)其他線程有消息發(fā)送到消息隊(duì)列時(shí)會(huì)通過(guò)管道來(lái)寫(xiě)數(shù)據(jù);
在我們調(diào)試程序時(shí),我們通過(guò)函數(shù)的調(diào)用棧就可以發(fā)現(xiàn)其中的道理:
這也印證了開(kāi)始的那句話——Android應(yīng)用程序是通過(guò)消息來(lái)驅(qū)動(dòng)的。
是否所有的消息都會(huì)在指定時(shí)間開(kāi)始執(zhí)行
這個(gè)問(wèn)題的意思是當(dāng)我們像消息隊(duì)列中發(fā)送消息時(shí)(比如延時(shí)1000ms執(zhí)行一個(gè)消息postDelay(action, 1000)),是不是會(huì)在1000ms后去執(zhí)行這個(gè)消息。
答案是不一定。我們只Android的消息是按照時(shí)間順序保存在消息隊(duì)列中的,如果我們向隊(duì)列中添加多個(gè)消息,比如10000個(gè)延時(shí)1000ms執(zhí)行的消息,那么其實(shí)最后一個(gè)執(zhí)行的消息和第一個(gè)執(zhí)行的消息的執(zhí)行時(shí)間是不一樣的。
總結(jié)
至此Android系統(tǒng)的消息處理機(jī)制就分析完畢了。在Android應(yīng)用程序中消息處理主要分為3個(gè)過(guò)程:
- 啟動(dòng)Looper中的消息循環(huán),開(kāi)始監(jiān)聽(tīng)消息隊(duì)列。
- 通過(guò)Handler發(fā)送消息到消息隊(duì)列。
- 通過(guò)消息循環(huán)調(diào)用Handler對(duì)象處理新加入的消息。
在使用消息隊(duì)列時(shí),主線程中在程序啟動(dòng)時(shí)就會(huì)創(chuàng)建消息隊(duì)列,所以我們使用主線程中的消息機(jī)制時(shí),不需要初始化消息循環(huán)和消息隊(duì)列。在子線程中我們需要初始化消息隊(duì)列,并且注意在不需要使用消息隊(duì)列時(shí),應(yīng)該及時(shí)調(diào)用Looper的quit或者quitSafely方法關(guān)閉消息循環(huán),否則子線程可能一直處于等待狀態(tài)。
以上就是詳解Android 消息處理機(jī)制的詳細(xì)內(nèi)容,更多關(guān)于Android 消息處理機(jī)制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#中利用正則表達(dá)式將人民幣金額轉(zhuǎn)換為大寫(xiě)漢字
這篇文章主要介紹了C#中利用正則表達(dá)式將人民幣金額轉(zhuǎn)換為大寫(xiě)漢字的方法,需要的朋友可以參考下2016-02-02Android 單例模式的四種實(shí)現(xiàn)方式
單例模式作為設(shè)計(jì)模式之一,使用場(chǎng)景非常多。本文講述了Android實(shí)現(xiàn)單例模式的幾種方式2021-05-05Android應(yīng)用中使用ViewPager實(shí)現(xiàn)類(lèi)似QQ的界面切換效果
這篇文章主要介紹了Android應(yīng)用中使用ViewPager實(shí)現(xiàn)類(lèi)似QQ的界面切換效果的示例,文中的例子重寫(xiě)了PagerAdapter,并且講解了如何解決Android下ViewPager和PagerAdapter中調(diào)用notifyDataSetChanged失效的問(wèn)題,需要的朋友可以參考下2016-03-03Android開(kāi)發(fā)之完全隱藏軟鍵盤(pán)的方法
這篇文章主要介紹了Android開(kāi)發(fā)之完全隱藏軟鍵盤(pán)的方法的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06Android中EditText setText方法的踩坑實(shí)戰(zhàn)
這篇文章主要給大家分享了一些關(guān)于Android中EditText setText方法的踩坑記錄,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位Android開(kāi)發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-07-07