Android應(yīng)用程序是通過消息來驅(qū)動的,系統(tǒng)為每一個應(yīng)用程序維護一個消息隊例,應(yīng)用程序的主線程不斷地從這個消息隊例中獲取消息(Looper),然后對這些消息進行處理(Handler),這樣就實現(xiàn)了通過消息來驅(qū)動應(yīng)用程序的執(zhí)行,本文將詳細(xì)分析Android應(yīng)用程序的消息處理機制。
前面我們學(xué)習(xí)Android應(yīng)用程序中的Activity啟動(Android應(yīng)用程序啟動過程源代碼分析和Android應(yīng)用程序內(nèi)部啟動Activity過程(startActivity)的源代碼分析)、Service啟動(Android系統(tǒng)在新進程中啟動自定義服務(wù)過程(startService)的原理分析和Android應(yīng)用程序綁定服務(wù)(bindService)的過程源代碼分析)以及廣播發(fā)送(Android應(yīng)用程序發(fā)送廣播(sendBroadcast)的過程分析)時,它們都有一個共同的特點,當(dāng)ActivityManagerService需要與應(yīng)用程序進行并互時,如加載Activity和Service、處理廣播待,會通過Binder進程間通信機制來知會應(yīng)用程序,應(yīng)用程序接收到這個請求時,它不是馬上就處理這個請求,而是將這個請求封裝成一個消息,然后把這個消息放在應(yīng)用程序的消息隊列中去,然后再通過消息循環(huán)來處理這個消息。這樣做的好處就是消息的發(fā)送方只要把消息發(fā)送到應(yīng)用程序的消息隊列中去就行了,它可以馬上返回去處理別的事情,而不需要等待消息的接收方去處理完這個消息才返回,這樣就可以提高系統(tǒng)的并發(fā)性。實質(zhì)上,這就是一種異步處理機制。
這樣說可能還是比較籠統(tǒng),我們以Android應(yīng)用程序啟動過程源代碼分析一文中所介紹的應(yīng)用程序啟動過程的一個片斷來具體看看是如何這種消息處理機制的。在這篇文章中,要啟動的應(yīng)用程序稱為Activity,它的默認(rèn)Activity是MainActivity,它是由Launcher來負(fù)責(zé)啟動的,而Launcher又是通過ActivityManagerService來啟動的,當(dāng)ActivityManagerService為這個即將要啟的應(yīng)用程序準(zhǔn)備好新的進程后,便通過一個Binder進程間通信過程來通知這個新的進程來加載MainActivity,如下圖所示:
它對應(yīng)Android應(yīng)用程序啟動過程中的Step 30到Step 35,有興趣的讀者可以回過頭去參考Android應(yīng)用程序啟動過程源代碼分析一文。這里的Step 30中的scheduleLaunchActivity是ActivityManagerService通過Binder進程間通信機制發(fā)送過來的請求,它請求應(yīng)用程序中的ActivityThread執(zhí)行Step 34中的performLaunchActivity操作,即啟動MainActivity的操作。這里我們就可以看到,Step 30的這個請求并沒有等待Step 34這個操作完成就返回了,它只是把這個請求封裝成一個消息,然后通過Step 31中的queueOrSendMessage操作把這個消息放到應(yīng)用程序的消息隊列中,然后就返回了。應(yīng)用程序發(fā)現(xiàn)消息隊列中有消息時,就會通過Step 32中的handleMessage操作來處理這個消息,即調(diào)用Step 33中的handleLaunchActivity來執(zhí)行實際的加載MainAcitivy類的操作。
了解Android應(yīng)用程序的消息處理過程之后,我們就開始分樣它的實現(xiàn)原理了。與Windows應(yīng)用程序的消息處理過程一樣,Android應(yīng)用程序的消息處理機制也是由消息循環(huán)、消息發(fā)送和消息處理這三個部分組成的,接下來,我們就詳細(xì)描述這三個過程。
1. 消息循環(huán)
在消息處理機制中,消息都是存放在一個消息隊列中去,而應(yīng)用程序的主線程就是圍繞這個消息隊列進入一個無限循環(huán)的,直到應(yīng)用程序退出。如果隊列中有消息,應(yīng)用程序的主線程就會把它取出來,并分發(fā)給相應(yīng)的Handler進行處理;如果隊列中沒有消息,應(yīng)用程序的主線程就會進入空閑等待狀態(tài),等待下一個消息的到來。在Android應(yīng)用程序中,這個消息循環(huán)過程是由Looper類來實現(xiàn)的,它定義在frameworks/base/core/java/android/os/Looper.java文件中,在分析這個類之前,我們先看一下Android應(yīng)用程序主線程是如何進入到這個消息循環(huán)中去的。
在Android應(yīng)用程序進程啟動過程的源代碼分析一文中,我們分析了Android應(yīng)用程序進程的啟動過程,Android應(yīng)用程序進程在啟動的時候,會在進程中加載ActivityThread類,并且執(zhí)行這個類的main函數(shù),應(yīng)用程序的消息循環(huán)過程就是在這個main函數(shù)里面實現(xiàn)的,我們來看看這個函數(shù)的實現(xiàn),它定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
public final class ActivityThread { ...... public static final void main(String[] args) { ...... Looper.prepareMainLooper(); ...... ActivityThread thread = new ActivityThread(); thread.attach(false); ...... Looper.loop(); ...... thread.detach(); ...... } }這個函數(shù)做了兩件事情,一是在主線程中創(chuàng)建了一個ActivityThread實例,二是通過Looper類使主線程進入消息循環(huán)中,這里我們只關(guān)注后者。
首先看Looper.prepareMainLooper函數(shù)的實現(xiàn),這是一個靜態(tài)成員函數(shù),定義在frameworks/base/core/java/android/os/Looper.java文件中:
public class Looper { ...... private static final ThreadLocal sThreadLocal = new ThreadLocal(); final MessageQueue mQueue; ...... /** 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()}. */ public static final void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper()); } /** 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. * {@link #prepare()} */ public static final void prepareMainLooper() { prepare(); setMainLooper(myLooper()); if (Process.supportsProcesses()) { myLooper().mQueue.mQuitAllowed = false; } } private synchronized static void setMainLooper(Looper looper) { mMainLooper = looper; } /** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static final Looper myLooper() { return (Looper)sThreadLocal.get(); } private Looper() { mQueue = new MessageQueue(); mRun = true; mThread = Thread.currentThread(); } ...... }函數(shù)prepareMainLooper做的事情其實就是在線程中創(chuàng)建一個Looper對象,這個Looper對象是存放在sThreadLocal成員變量里面的,成員變量sThreadLocal的類型為ThreadLocal,表示這是一個線程局部變量,即保證每一個調(diào)用了prepareMainLooper函數(shù)的線程里面都有一個獨立的Looper對象。在線程是創(chuàng)建Looper對象的工作是由prepare函數(shù)來完成的,而在創(chuàng)建Looper對象的時候,會同時創(chuàng)建一個消息隊列MessageQueue,保存在Looper的成員變量mQueue中,后續(xù)消息就是存放在這個隊列中去。消息隊列在Android應(yīng)用程序消息處理機制中最重要的組件,因此,我們看看它的創(chuàng)建過程,即它的構(gòu)造函數(shù)的實現(xiàn),實現(xiàn)frameworks/base/core/java/android/os/MessageQueue.java文件中:
public class MessageQueue { ...... private int mPtr; // used by native code private native void nativeInit(); MessageQueue() { nativeInit(); } ...... }它的初始化工作都交給JNI方法nativeInit來實現(xiàn)了,這個JNI方法定義在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:
static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) { NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); if (! nativeMessageQueue) { jniThrowRuntimeException(env, "Unable to allocate native queue"); return; } android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue); }在JNI中,也相應(yīng)地創(chuàng)建了一個消息隊列NativeMessageQueue,NativeMessageQueue類也是定義在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中,它的創(chuàng)建過程如下所示:
NativeMessageQueue::NativeMessageQueue() { mLooper = Looper::getForThread(); if (mLooper == NULL) { mLooper = new Looper(false); Looper::setForThread(mLooper); } }它主要就是在內(nèi)部創(chuàng)建了一個Looper對象,注意,這個Looper對象是實現(xiàn)在JNI層的,它與上面Java層中的Looper是不一樣的,不過它們是對應(yīng)的,下面我們進一步分析消息循環(huán)的過程的時候,讀者就會清楚地了解到它們之間的關(guān)系。
這個Looper的創(chuàng)建過程也很重要,不過我們暫時放一放,先分析完android_os_MessageQueue_nativeInit函數(shù)的執(zhí)行,它創(chuàng)建了本地消息隊列NativeMessageQueue對象之后,接著調(diào)用android_os_MessageQueue_setNativeMessageQueue函數(shù)來把這個消息隊列對象保存在前面我們在Java層中創(chuàng)建的MessageQueue對象的mPtr成員變量里面:
static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj, NativeMessageQueue* nativeMessageQueue) { env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr, reinterpret_cast<jint>(nativeMessageQueue)); }這里傳進來的參數(shù)messageQueueObj即為我們前面在Java層創(chuàng)建的消息隊列對象,而gMessageQueueClassInfo.mPtr即表示在Java類MessageQueue中,其成員變量mPtr的偏移量,通過這個偏移量,就可以把這個本地消息隊列對象natvieMessageQueue保存在Java層創(chuàng)建的消息隊列對象的mPtr成員變量中,這是為了后續(xù)我們調(diào)用Java層的消息隊列對象的其它成員函數(shù)進入到JNI層時,能夠方便地找回它在JNI層所對應(yīng)的消息隊列對象。
我們再回到NativeMessageQueue的構(gòu)造函數(shù)中,看看JNI層的Looper對象的創(chuàng)建過程,即看看它的構(gòu)造函數(shù)是如何實現(xiàn)的,這個Looper類實現(xiàn)在frameworks/base/libs/utils/Looper.cpp文件中:
Looper::Looper(bool allowNonCallbacks) : mAllowNonCallbacks(allowNonCallbacks), mResponseIndex(0) { int wakeFds[2]; int result = pipe(wakeFds); ...... mWakeReadPipeFd = wakeFds[0]; mWakeWritePipeFd = wakeFds[1]; ...... #ifdef LOOPER_USES_EPOLL // Allocate the epoll instance and register the wake pipe. mEpollFd = epoll_create(EPOLL_SIZE_HINT); ...... struct epoll_event eventItem; memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union eventItem.events = EPOLLIN; eventItem.data.fd = mWakeReadPipeFd; result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem); ...... #else ...... #endif ...... }這個構(gòu)造函數(shù)做的事情非常重要,它跟我們后面要介紹的應(yīng)用程序主線程在消息隊列中沒有消息時要進入等待狀態(tài)以及當(dāng)消息隊列有消息時要把應(yīng)用程序主線程喚醒的這兩個知識點息息相關(guān)。它主要就是通過pipe系統(tǒng)調(diào)用來創(chuàng)建了一個管道了:
int wakeFds[2]; int result = pipe(wakeFds); ...... mWakeReadPipeFd = wakeFds[0]; mWakeWritePipeFd = wakeFds[1];管道是Linux系統(tǒng)中的一種進程間通信機制,具體可以參考前面一篇文章Android學(xué)習(xí)啟動篇推薦的一本書《Linux內(nèi)核源代碼情景分析》中的第6章--傳統(tǒng)的Uinx進程間通信。簡單來說,管道就是一個文件,在管道的兩端,分別是兩個打開文件文件描述符,這兩個打開文件描述符都是對應(yīng)同一個文件,其中一個是用來讀的,別一個是用來寫的,一般的使用方式就是,一個線程通過讀文件描述符中來讀管道的內(nèi)容,當(dāng)管道沒有內(nèi)容時,這個線程就會進入等待狀態(tài),而另外一個線程通過寫文件描述符來向管道中寫入內(nèi)容,寫入內(nèi)容的時候,如果另一端正有線程正在等待管道中的內(nèi)容,那么這個線程就會被喚醒。這個等待和喚醒的操作是如何進行的呢,這就要借助Linux系統(tǒng)中的epoll機制了。 Linux系統(tǒng)中的epoll機制為處理大批量句柄而作了改進的poll,是Linux下多路復(fù)用IO接口select/poll的增強版本,它能顯著減少程序在大量并發(fā)連接中只有少量活躍的情況下的系統(tǒng)CPU利用率。但是這里我們其實只需要監(jiān)控的IO接口只有mWakeReadPipeFd一個,即前面我們所創(chuàng)建的管道的讀端,為什么還需要用到epoll呢?有點用牛刀來殺雞的味道。其實不然,這個Looper類是非常強大的,它除了監(jiān)控內(nèi)部所創(chuàng)建的管道接口之外,還提供了addFd接口供外界面調(diào)用,外界可以通過這個接口把自己想要監(jiān)控的IO事件一并加入到這個Looper對象中去,當(dāng)所有這些被監(jiān)控的IO接口上面有事件發(fā)生時,就會喚醒相應(yīng)的線程來處理,不過這里我們只關(guān)心剛才所創(chuàng)建的管道的IO事件的發(fā)生。
要使用Linux系統(tǒng)的epoll機制,首先要通過epoll_create來創(chuàng)建一個epoll專用的文件描述符:
mEpollFd = epoll_create(EPOLL_SIZE_HINT);傳入的參數(shù)EPOLL_SIZE_HINT是在這個mEpollFd上能監(jiān)控的最大文件描述符數(shù)。
接著還要通過epoll_ctl函數(shù)來告訴epoll要監(jiān)控相應(yīng)的文件描述符的什么事件:
struct epoll_event eventItem; memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union eventItem.events = EPOLLIN; eventItem.data.fd = mWakeReadPipeFd; result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);這里就是告訴mEpollFd,它要監(jiān)控mWakeReadPipeFd文件描述符的EPOLLIN事件,即當(dāng)管道中有內(nèi)容可讀時,就喚醒當(dāng)前正在等待管道中的內(nèi)容的線程。
C++層的這個Looper對象創(chuàng)建好了之后,就返回到JNI層的NativeMessageQueue的構(gòu)造函數(shù),最后就返回到Java層的消息隊列MessageQueue的創(chuàng)建過程,這樣,Java層的Looper對象就準(zhǔn)備好了。有點復(fù)雜,我們先小結(jié)一下這一步都做了些什么事情:
A. 在Java層,創(chuàng)建了一個Looper對象,這個Looper對象是用來進入消息循環(huán)的,它的內(nèi)部有一個消息隊列MessageQueue對象mQueue;
B. 在JNI層,創(chuàng)建了一個NativeMessageQueue對象,這個NativeMessageQueue對象保存在Java層的消息隊列對象mQueue的成員變量mPtr中;
C. 在C++層,創(chuàng)建了一個Looper對象,保存在JNI層的NativeMessageQueue對象的成員變量mLooper中,這個對象的作用是,當(dāng)Java層的消息隊列中沒有消息時,就使Android應(yīng)用程序主線程進入等待狀態(tài),而當(dāng)Java層的消息隊列中來了新的消息后,就喚醒Android應(yīng)用程序的主線程來處理這個消息。
回到ActivityThread類的main函數(shù)中,在上面這些工作都準(zhǔn)備好之后,就調(diào)用Looper類的loop函數(shù)進入到消息循環(huán)中去了:
public class Looper { ...... public static final void loop() { Looper me = myLooper(); MessageQueue queue = me.mQueue; ...... while (true) { Message msg = queue.next(); // might block ...... if (msg != null) { if (msg.target == null) { // No target is a magic identifier for the quit message. return; } ...... msg.target.dispatchMessage(msg); ...... msg.recycle(); } } } ...... }這里就是進入到消息循環(huán)中去了,它不斷地從消息隊列mQueue中去獲取下一個要處理的消息msg,如果消息的target成員變量為null,就表示要退出消息循環(huán)了,否則的話就要調(diào)用這個target對象的dispatchMessage成員函數(shù)來處理這個消息,這個target對象的類型為Handler,下面我們分析消息的發(fā)送時會看到這個消息對象msg是如設(shè)置的。
這個函數(shù)最關(guān)鍵的地方便是從消息隊列中獲取下一個要處理的消息了,即MessageQueue.next函數(shù),它實現(xiàn)frameworks/base/core/java/android/os/MessageQueue.java文件中:
public class MessageQueue { ...... final Message next() { int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(mPtr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); final Message msg = mMessages; if (msg != null) { final long when = msg.when; if (now >= when) { mBlocked = false; mMessages = msg.next; msg.next = null; if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg); return msg; } else { nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE); } } else { nextPollTimeoutMillis = -1; } // If first time, then get the number of idlers to run. if (pendingIdleHandlerCount < 0) { 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("MessageQueue", "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; } } ...... }調(diào)用這個函數(shù)的時候,有可能會讓線程進入等待狀態(tài)。什么情況下,線程會進入等待狀態(tài)呢?兩種情況,一是當(dāng)消息隊列中沒有消息時,它會使線程進入等待狀態(tài);二是消息隊列中有消息,但是消息指定了執(zhí)行的時間,而現(xiàn)在還沒有到這個時間,線程也會進入等待狀態(tài)。消息隊列中的消息是按時間先后來排序的,后面我們在分析消息的發(fā)送時會看到。
執(zhí)行下面語句是看看當(dāng)前消息隊列中有沒有消息:
nativePollOnce(mPtr, nextPollTimeoutMillis);這是一個JNI方法,我們等一下再分析,這里傳入的參數(shù)mPtr就是指向前面我們在JNI層創(chuàng)建的NativeMessageQueue對象了,而參數(shù)nextPollTimeoutMillis則表示如果當(dāng)前消息隊列中沒有消息,它要等待的時候,for循環(huán)開始時,傳入的值為0,表示不等待。
當(dāng)前nativePollOnce返回后,就去看看消息隊列中有沒有消息:
final Message msg = mMessages; if (msg != null) { final long when = msg.when; if (now >= when) { mBlocked = false; mMessages = msg.next; msg.next = null; if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg); return msg; } else { nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE); } } else { nextPollTimeoutMillis = -1; }如果消息隊列中有消息,并且當(dāng)前時候大于等于消息中的執(zhí)行時間,那么就直接返回這個消息給Looper.loop消息處理,否則的話就要等待到消息的執(zhí)行時間:
nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);如果消息隊列中沒有消息,那就要進入無窮等待狀態(tài)直到有新消息了:
nextPollTimeoutMillis = -1;-1表示下次調(diào)用nativePollOnce時,如果消息中沒有消息,就進入無限等待狀態(tài)中去。
這里計算出來的等待時間都是在下次調(diào)用nativePollOnce時使用的。
這里說的等待,是空閑等待,而不是忙等待,因此,在進入空閑等待狀態(tài)前,如果應(yīng)用程序注冊了IdleHandler接口來處理一些事情,那么就會先執(zhí)行這里IdleHandler,然后再進入等待狀態(tài)。IdlerHandler是定義在MessageQueue的一個內(nèi)部類:
public class MessageQueue { ...... /** * Callback interface for discovering when a thread is going to block * waiting for more messages. */ public static interface IdleHandler { /** * Called when the message queue has run out of messages and will now * wait for more. Return true to keep your idle handler active, false * to have it removed. This may be called if there are still messages * pending in the queue, but they are all scheduled to be dispatched * after the current time. */ boolean queueIdle(); } ...... }它只有一個成員函數(shù)queueIdle,執(zhí)行這個函數(shù)時,如果返回值為false,那么就會從應(yīng)用程序中移除這個IdleHandler,否則的話就會在應(yīng)用程序中繼續(xù)維護著這個IdleHandler,下次空閑時仍會再執(zhí)會這個IdleHandler。MessageQueue提供了addIdleHandler和removeIdleHandler兩注冊和刪除IdleHandler。
回到MessageQueue函數(shù)中,它接下來就是在進入等待狀態(tài)前,看看有沒有IdleHandler是需要執(zhí)行的:
// If first time, then get the number of idlers to run. if (pendingIdleHandlerCount < 0) { 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);如果沒有,即pendingIdleHandlerCount等于0,那下面的邏輯就不執(zhí)行了,通過continue語句直接進入下一次循環(huán),否則就要把注冊在mIdleHandlers中的IdleHandler取出來,放在mPendingIdleHandlers數(shù)組中去。
接下來就是執(zhí)行這些注冊了的IdleHanlder了:
// 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("MessageQueue", "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } }執(zhí)行完這些IdleHandler之后,線程下次調(diào)用nativePollOnce函數(shù)時,就不設(shè)置超時時間了,因為,很有可能在執(zhí)行IdleHandler的時候,已經(jīng)有新的消息加入到消息隊列中去了,因此,要重置nextPollTimeoutMillis的值:
// 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;分析完MessageQueue的這個next函數(shù)之后,我們就要深入分析一下JNI方法nativePollOnce了,看看它是如何進入等待狀態(tài)的,這個函數(shù)定義在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, jint ptr, jint timeoutMillis) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); nativeMessageQueue->pollOnce(timeoutMillis); }這個函數(shù)首先是通過傳進入的參數(shù)ptr取回前面在Java層創(chuàng)建MessageQueue對象時在JNI層創(chuàng)建的NatvieMessageQueue對象,然后調(diào)用它的pollOnce函數(shù):
void NativeMessageQueue::pollOnce(int timeoutMillis) { mLooper->pollOnce(timeoutMillis); }這里將操作轉(zhuǎn)發(fā)給mLooper對象的pollOnce函數(shù)處理,這里的mLooper對象是在C++層的對象,它也是在前面在JNI層創(chuàng)建的NatvieMessageQueue對象時創(chuàng)建的,它的pollOnce函數(shù)定義在frameworks/base/libs/utils/Looper.cpp文件中:
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) { int result = 0; for (;;) { ...... if (result != 0) { ...... return result; } result = pollInner(timeoutMillis); } }為了方便討論,我們把這個函數(shù)的無關(guān)部分都去掉,它主要就是調(diào)用pollInner函數(shù)來進一步操作,如果pollInner返回值不等于0,這個函數(shù)就可以返回了。
函數(shù)pollInner的定義如下:
int Looper::pollInner(int timeoutMillis) { ...... int result = ALOOPER_POLL_WAKE; ...... #ifdef LOOPER_USES_EPOLL struct epoll_event eventItems[EPOLL_MAX_EVENTS]; int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); bool acquiredLock = false; #else ...... #endif if (eventCount < 0) { if (errno == EINTR) { goto Done; } LOGW("Poll failed with an unexpected error, errno=%d", errno); result = ALOOPER_POLL_ERROR; goto Done; } if (eventCount == 0) { ...... result = ALOOPER_POLL_TIMEOUT; goto Done; } ...... #ifdef LOOPER_USES_EPOLL for (int i = 0; i < eventCount; i++) { int fd = eventItems[i].data.fd; uint32_t epollEvents = eventItems[i].events; if (fd == mWakeReadPipeFd) { if (epollEvents & EPOLLIN) { awoken(); } else { LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); } } else { ...... } } if (acquiredLock) { mLock.unlock(); } Done: ; #else ...... #endif ...... return result; }這里,首先是調(diào)用epoll_wait函數(shù)來看看epoll專用文件描述符mEpollFd所監(jiān)控的文件描述符是否有IO事件發(fā)生,它設(shè)置監(jiān)控的超時時間為timeoutMillis:
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);回憶一下前面的Looper的構(gòu)造函數(shù),我們在里面設(shè)置了要監(jiān)控mWakeReadPipeFd文件描述符的EPOLLIN事件。
當(dāng)mEpollFd所監(jiān)控的文件描述符發(fā)生了要監(jiān)控的IO事件后或者監(jiān)控時間超時后,線程就從epoll_wait返回了,否則線程就會在epoll_wait函數(shù)中進入睡眠狀態(tài)了。返回后如果eventCount等于0,就說明是超時了:
if (eventCount == 0) { ...... result = ALOOPER_POLL_TIMEOUT; goto Done; }如果eventCount不等于0,就說明發(fā)生要監(jiān)控的事件:
for (int i = 0; i < eventCount; i++) { int fd = eventItems[i].data.fd; uint32_t epollEvents = eventItems[i].events; if (fd == mWakeReadPipeFd) { if (epollEvents & EPOLLIN) { awoken(); } else { LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); } } else { ...... } }這里我們只關(guān)注mWakeReadPipeFd文件描述符上的事件,如果在mWakeReadPipeFd文件描述符上發(fā)生了EPOLLIN就說明應(yīng)用程序中的消息隊列里面有新的消息需要處理了,接下來它就會先調(diào)用awoken函數(shù)清空管道中把內(nèi)容,以便下次再調(diào)用pollInner函數(shù)時,知道自從上次處理完消息隊列中的消息后,有沒有新的消息加進來。
函數(shù)awoken的實現(xiàn)很簡單,它只是把管道中的內(nèi)容都讀取出來:
void Looper::awoken() { ...... char buffer[16]; ssize_t nRead; do { nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer)); }因為當(dāng)其它的線程向應(yīng)用程序的消息隊列加入新的消息時,會向這個管道寫入新的內(nèi)容來通知應(yīng)用程序主線程有新的消息需要處理了,下面我們分析消息的發(fā)送的時候?qū)吹健?p>
這樣,消息的循環(huán)過程就分析完了,這部分邏輯還是比較復(fù)雜的,它利用Linux系統(tǒng)中的管道(pipe)進程間通信機制來實現(xiàn)消息的等待和處理,不過,了解了這部分內(nèi)容之后,下面我們分析消息的發(fā)送和處理就簡單多了。
2. 消息的發(fā)送
應(yīng)用程序的主線程準(zhǔn)備就好消息隊列并且進入到消息循環(huán)后,其它地方就可以往這個消息隊列中發(fā)送消息了。我們繼續(xù)以文章開始介紹的Android應(yīng)用程序啟動過程源代碼分析一文中的應(yīng)用程序啟動過為例,說明應(yīng)用程序是如何把消息加入到應(yīng)用程序的消息隊列中去的。
在Android應(yīng)用程序啟動過程源代碼分析這篇文章的Step 30中,ActivityManagerService通過調(diào)用ApplicationThread類的scheduleLaunchActivity函數(shù)通知應(yīng)用程序,它可以加載應(yīng)用程序的默認(rèn)Activity了,這個函數(shù)定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
public final class ActivityThread { ...... private final class ApplicationThread extends ApplicationThreadNative { ...... // we use token to identify this activity without having to send the // activity itself back to the activity manager. (matters more with ipc) public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Bundle state, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, boolean isForward) { ActivityClientRecord r = new ActivityClientRecord(); r.token = token; r.ident = ident; r.intent = intent; r.activityInfo = info; r.state = state; r.pendingResults = pendingResults; r.pendingIntents = pendingNewIntents; r.startsNotResumed = notResumed; r.isForward = isForward; queueOrSendMessage(H.LAUNCH_ACTIVITY, r); } ...... } ...... }這里把相關(guān)的參數(shù)都封裝成一個ActivityClientRecord對象r,然后調(diào)用queueOrSendMessage函數(shù)來往應(yīng)用程序的消息隊列中加入一個新的消息(H.LAUNCH_ACTIVITY),這個函數(shù)定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
public final class ActivityThread { ...... private final class ApplicationThread extends ApplicationThreadNative { ...... // if the thread hasn't started yet, we don't have the handler, so just // save the messages until we're ready. private final void queueOrSendMessage(int what, Object obj) { queueOrSendMessage(what, obj, 0, 0); } ...... private final void queueOrSendMessage(int what, Object obj, int arg1, int arg2) { synchronized (this) { ...... Message msg = Message.obtain(); msg.what = what; msg.obj = obj; msg.arg1 = arg1; msg.arg2 = arg2; mH.sendMessage(msg); } } ...... } ...... }在queueOrSendMessage函數(shù)中,又進一步把上面?zhèn)鬟M來的參數(shù)封裝成一個Message對象msg,然后通過mH.sendMessage函數(shù)把這個消息對象msg加入到應(yīng)用程序的消息隊列中去。這里的mH是ActivityThread類的成員變量,它的類型為H,繼承于Handler類,它定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
public final class ActivityThread { ...... private final class H extends Handler { ...... public void handleMessage(Message msg) { ...... switch (msg.what) { ...... } ...... } ...... }
這個H類就是通過其成員函數(shù)handleMessage函數(shù)來處理消息的了,后面我們分析消息的處理過程時會看到。
ActivityThread類的這個mH成員變量是什么時候創(chuàng)建的呢?我們前面在分析應(yīng)用程序的消息循環(huán)時,說到當(dāng)應(yīng)用程序進程啟動之后,就會加載ActivityThread類的main函數(shù)里面,在這個main函數(shù)里面,在通過Looper類進入消息循環(huán)之前,會在當(dāng)前進程中創(chuàng)建一個ActivityThread實例:
public final class ActivityThread { ...... public static final void main(String[] args) { ...... ActivityThread thread = new ActivityThread(); thread.attach(false); ...... } }在創(chuàng)建這個實例的時候,就會同時創(chuàng)建其成員變量mH了:
public final class ActivityThread { ...... final H mH = new H(); ...... }前面說過,H類繼承于Handler類,因此,當(dāng)創(chuàng)建這個H對象時,會調(diào)用Handler類的構(gòu)造函數(shù),這個函數(shù)定義在frameworks/base/core/java/android/os/Handler.java文件中:
public class Handler { ...... public Handler() { ...... mLooper = Looper.myLooper(); ...... mQueue = mLooper.mQueue; ...... } final MessageQueue mQueue; final Looper mLooper; ...... }在Hanlder類的構(gòu)造函數(shù)中,主要就是初始成員變量mLooper和mQueue了。這里的myLooper是Looper類的靜態(tài)成員函數(shù),通過它來獲得一個Looper對象,這個Looper對象就是前面我們在分析消息循環(huán)時,在ActivityThread類的main函數(shù)中通過Looper.prepareMainLooper函數(shù)創(chuàng)建的。Looper.myLooper函數(shù)實現(xiàn)在frameworks/base/core/java/android/os/Looper.java文件中:
public class Looper { ...... public static final Looper myLooper() { return (Looper)sThreadLocal.get(); } ...... }有了這個Looper對象后,就可以通過Looper.mQueue來訪問應(yīng)用程序的消息隊列了。
有了這個Handler對象mH后,就可以通過它來往應(yīng)用程序的消息隊列中加入新的消息了。回到前面的queueOrSendMessage函數(shù)中,當(dāng)它準(zhǔn)備好了一個Message對象msg后,就開始調(diào)用mH.sendMessage函數(shù)來發(fā)送消息了,這個函數(shù)定義在frameworks/base/core/java/android/os/Handler.java文件中:
public class Handler { ...... 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) { boolean sent = false; MessageQueue queue = mQueue; if (queue != null) { msg.target = this; sent = queue.enqueueMessage(msg, uptimeMillis); } else { ...... } return sent; } ...... }在發(fā)送消息時,是可以指定消息的處理時間的,但是通過sendMessage函數(shù)發(fā)送的消息的處理時間默認(rèn)就為當(dāng)前時間,即表示要馬上處理,因此,從sendMessage函數(shù)中調(diào)用sendMessageDelayed函數(shù),傳入的時間參數(shù)為0,表示這個消息不要延時處理,而在sendMessageDelayed函數(shù)中,則會先獲得當(dāng)前時間,然后加上消息要延時處理的時間,即得到這個處理這個消息的絕對時間,然后調(diào)用sendMessageAtTime函數(shù)來把消息加入到應(yīng)用程序的消息隊列中去。
在sendMessageAtTime函數(shù),首先得到應(yīng)用程序的消息隊列mQueue,這是在Handler對象構(gòu)造時初始化好的,前面已經(jīng)分析過了,接著設(shè)置這個消息的目標(biāo)對象target,即這個消息最終是由誰來處理的:
msg.target = this;這里將它賦值為this,即表示這個消息最終由這個Handler對象來處理,即由ActivityThread對象的mH成員變量來處理。
函數(shù)最后調(diào)用queue.enqueueMessage來把這個消息加入到應(yīng)用程序的消息隊列中去,這個函數(shù)實現(xiàn)在frameworks/base/core/java/android/os/MessageQueue.java文件中:
public class MessageQueue { ...... final boolean enqueueMessage(Message msg, long when) { ...... final boolean needWake; synchronized (this) { ...... msg.when = when; //Log.d("MessageQueue", "Enqueing: " + msg); Message p = mMessages; if (p == null || when == 0 || when < p.when) { msg.next = p; mMessages = msg; needWake = mBlocked; // new head, might need to wake up } else { Message prev = null; while (p != null && p.when <= when) { prev = p; p = p.next; } msg.next = prev.next; prev.next = msg; needWake = false; // still waiting on head, no need to wake up } } if (needWake) { nativeWake(mPtr); } return true; } ...... }把消息加入到消息隊列時,分兩種情況,一種當(dāng)前消息隊列為空時,這時候應(yīng)用程序的主線程一般就是處于空閑等待狀態(tài)了,這時候就要喚醒它,另一種情況是應(yīng)用程序的消息隊列不為空,這時候就不需要喚醒應(yīng)用程序的主線程了,因為這時候它一定是在忙著處于消息隊列中的消息,因此不會處于空閑等待的狀態(tài)。
第一種情況比較簡單,只要把消息放在消息隊列頭就可以了:
msg.next = p; mMessages = msg; needWake = mBlocked; // new head, might need to wake up第二種情況相對就比較復(fù)雜一些了,前面我們說過,當(dāng)往消息隊列中發(fā)送消息時,是可以指定消息的處理時間的,而消息隊列中的消息,就是按照這個時間從小到大來排序的,因此,當(dāng)把新的消息加入到消息隊列時,就要根據(jù)它的處理時間來找到合適的位置,然后再放進消息隊列中去:
Message prev = null; while (p != null && p.when <= when) { prev = p; p = p.next; } msg.next = prev.next; prev.next = msg; needWake = false; // still waiting on head, no need to wake up把消息加入到消息隊列去后,如果應(yīng)用程序的主線程正處于空閑等待狀態(tài),就需要調(diào)用natvieWake函數(shù)來喚醒它了,這是一個JNI方法,定義在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jobject obj, jint ptr) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); return nativeMessageQueue->wake(); }這個JNI層的NativeMessageQueue對象我們在前面分析消息循環(huán)的時候創(chuàng)建好的,保存在Java層的MessageQueue對象的mPtr成員變量中,這里把它取回來之后,就調(diào)用它的wake函數(shù)來喚醒應(yīng)用程序的主線程,這個函數(shù)也是定義在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:
void NativeMessageQueue::wake() { mLooper->wake(); }這里它又通過成員變量mLooper的wake函數(shù)來執(zhí)行操作,這里的mLooper成員變量是一個C++層實現(xiàn)的Looper對象,它定義在frameworks/base/libs/utils/Looper.cpp文件中:
void Looper::wake() { ...... ssize_t nWrite; do { nWrite = write(mWakeWritePipeFd, "W", 1); } while (nWrite == -1 && errno == EINTR); ....... }這個wake函數(shù)很簡單,只是通過打開文件描述符mWakeWritePipeFd往管道的寫入一個"W"字符串。其實,往管道寫入什么內(nèi)容并不重要,往管道寫入內(nèi)容的目的是為了喚醒應(yīng)用程序的主線程。前面我們在分析應(yīng)用程序的消息循環(huán)時說到,當(dāng)應(yīng)用程序的消息隊列中沒有消息處理時,應(yīng)用程序的主線程就會進入空閑等待狀態(tài),而這個空閑等待狀態(tài)就是通過調(diào)用這個Looper類的pollInner函數(shù)來進入的,具體就是在pollInner函數(shù)中調(diào)用epoll_wait函數(shù)來等待管道中有內(nèi)容可讀的。
這時候既然管道中有內(nèi)容可讀了,應(yīng)用程序的主線程就會從這里的Looper類的pollInner函數(shù)返回到JNI層的nativePollOnce函數(shù),最后返回到Java層中的MessageQueue.next函數(shù)中去,這里它就會發(fā)現(xiàn)消息隊列中有新的消息需要處理了,于就會處理這個消息。
3. 消息的處理
前面在分析消息循環(huán)時,說到應(yīng)用程序的主線程是在Looper類的loop成員函數(shù)中進行消息循環(huán)過程的,這個函數(shù)定義在frameworks/base/core/java/android/os/Looper.java文件中:
public class Looper { ...... public static final void loop() { Looper me = myLooper(); MessageQueue queue = me.mQueue; ...... while (true) { Message msg = queue.next(); // might block ...... if (msg != null) { if (msg.target == null) { // No target is a magic identifier for the quit message. return; } ...... msg.target.dispatchMessage(msg); ...... msg.recycle(); } } } ...... }它從消息隊列中獲得消息對象msg后,就會調(diào)用它的target成員變量的dispatchMessage函數(shù)來處理這個消息。在前面分析消息的發(fā)送時說過,這個消息對象msg的成員變量target是在發(fā)送消息的時候設(shè)置好的,一般就通過哪個Handler來發(fā)送消息,就通過哪個Handler來處理消息。
我們繼續(xù)以前面分析消息的發(fā)送時所舉的例子來分析消息的處理過程。前面說到,在Android應(yīng)用程序啟動過程源代碼分析這篇文章的Step 30中,ActivityManagerService通過調(diào)用ApplicationThread類的scheduleLaunchActivity函數(shù)通知應(yīng)用程序,它可以加載應(yīng)用程序的默認(rèn)Activity了,而ApplicationThread類的scheduleLaunchActivity函數(shù)最終把這個請求封裝成一個消息,然后通過ActivityThread類的成員變量mH來把這個消息加入到應(yīng)用程序的消息隊列中去。現(xiàn)在要對這個消息進行處理了,于是就會調(diào)用H類的dispatchMessage函數(shù)進行處理。
H類沒有實現(xiàn)自己的dispatchMessage函數(shù),但是它繼承了父類Handler的dispatchMessage函數(shù),這個函數(shù)定義在frameworks/base/core/java/android/os/ Handler.java文件中:
public class Handler { ...... public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } ...... }這里的消息對象msg的callback成員變量和Handler類的mCallBack成員變量一般都為null,于是,就會調(diào)用Handler類的handleMessage函數(shù)來處理這個消息,由于H類在繼承Handler類時,重寫了handleMessage函數(shù),因此,這里調(diào)用的實際上是H類的handleMessage函數(shù),這個函數(shù)定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
public final class ActivityThread { ...... private final class H extends Handler { ...... public void handleMessage(Message msg) { ...... switch (msg.what) { case LAUNCH_ACTIVITY: { ActivityClientRecord r = (ActivityClientRecord)msg.obj; r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo); handleLaunchActivity(r, null); } break; ...... } ...... } ...... }因為前面在分析消息的發(fā)送時所舉的例子中,發(fā)送的消息的類型為H.LAUNCH_ACTIVITY,因此,這里就會調(diào)用ActivityThread類的handleLaunchActivity函數(shù)來真正地處理這個消息了,后面的具體過程就可以參考Android應(yīng)用程序啟動過程源代碼分析這篇文章了。
至此,我們就從消息循環(huán)、消息發(fā)送和消息處理三個部分分析完Android應(yīng)用程序的消息處理機制了,為了更深理解,這里我們對其中的一些要點作一個總結(jié):
A. Android應(yīng)用程序的消息處理機制由消息循環(huán)、消息發(fā)送和消息處理三個部分組成的。
B. Android應(yīng)用程序的主線程在進入消息循環(huán)過程前,會在內(nèi)部創(chuàng)建一個Linux管道(Pipe),這個管道的作用是使得Android應(yīng)用程序主線程在消息隊列為空時可以進入空閑等待狀態(tài),并且使得當(dāng)應(yīng)用程序的消息隊列有消息需要處理時喚醒應(yīng)用程序的主線程。
C. Android應(yīng)用程序的主線程進入空閑等待狀態(tài)的方式實際上就是在管道的讀端等待管道中有新的內(nèi)容可讀,具體來說就是是通過Linux系統(tǒng)的Epoll機制中的epoll_wait函數(shù)進行的。
D. 當(dāng)往Android應(yīng)用程序的消息隊列中加入新的消息時,會同時往管道中的寫端寫入內(nèi)容,通過這種方式就可以喚醒正在等待消息到來的應(yīng)用程序主線程。
E. 當(dāng)應(yīng)用程序主線程在進入空閑等待前,會認(rèn)為當(dāng)前線程處理空閑狀態(tài),于是就會調(diào)用那些已經(jīng)注冊了的IdleHandler接口,使得應(yīng)用程序有機會在空閑的時候處理一些事情。