文章轉載至CSDN社區羅升陽的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6817933
Android應用程序是通過消息來驅動的,系統為每一個應用程序維護一個消息隊例,應用程序的主線程不斷地從這個消息 隊例中獲取消息(Looper),然后對這些消息進行處理(Handler),這樣就實現了通過消息來驅動應用程序的執行,本文將詳細分析Android 應用程序的消息處理機制。
?? ? ? ?前面我們學習Android應用程序中的Activity啟動(Android應用程序啟動過程源代碼分析和Android應用程序內部啟動Activity過程(startActivity)的源代碼分析)、Service啟動(Android系統在新進程中啟動自定義服務過程(startService)的原理分析和Android應用程序綁定服務(bindService)的過程源代碼分析)以及廣播發送(Android應用程序發送廣播(sendBroadcast)的過程分析)時,它們都有一個共同的特點,當ActivityManagerService需要與應用程序進行并互時,如加載Activity和Service、處理廣播待,會通過Binder進程間通信機制來 知會應用程序,應用程序接收到這個請求時,它不是馬上就處理這個請求,而是將這個請求封裝成一個消息,然后把這個消息放在應用程序的消息隊列中去,然后再 通過消息循環來處理這個消息。這樣做的好處就是消息的發送方只要把消息發送到應用程序的消息隊列中去就行了,它可以馬上返回去處理別的事情,而不需要等待 消息的接收方去處理完這個消息才返回,這樣就可以提高系統的并發性。實質上,這就是一種異步處理機制。
?? ? ? ?這樣說可能還是比較籠統,我們以Android應用程序啟動過程源代碼分析一文中所介紹的應用程序啟動過程的一個片斷來具體看看是如何這種消息處理機制的。在這篇文章中,要啟動的應用程序稱為Activity,它的默認Activity是MainActivity,它是由Launcher來負責啟動的,而Launcher又是通過ActivityManagerService來啟動的,當ActivityManagerService為這個即將要啟的應用程序準備好新的進程后,便通過一個Binder進程間通信過程來通知這個新的進程來加載MainActivity,如下圖所示:
?? ? ? ?它對應Android應用程序啟動過程中的Step 30到Step 35,有興趣的讀者可以回過頭去參考Android應用程序啟動過程源代碼分析一文。這里的Step 30中的scheduleLaunchActivity是ActivityManagerService通過Binder進程間通信機制發 送過來的請求,它請求應用程序中的ActivityThread執行Step 34中的performLaunchActivity操作,即啟動MainActivity的操作。這里我們就可以看到,Step 30的這個請求并沒有等待Step 34這個操作完成就返回了,它只是把這個請求封裝成一個消息,然后通過Step 31中的queueOrSendMessage操作把這個消息放到應用程序的消息隊列中,然后就返回了。應用程序發現消息隊列中有消息時,就會通過 Step 32中的handleMessage操作來處理這個消息,即調用Step 33中的handleLaunchActivity來執行實際的加載MainAcitivy類的操作。
?? ? ? ?了解Android應用程序的消息處理過程之后,我們就開始分樣它的實現原理了。與Windows應用程序的消息處理過程一樣,Android應用程序 的消息處理機制也是由消息循環、消息發送和消息處理這三個部分組成的,接下來,我們就詳細描述這三個過程。
?? ? ? ?1. 消息循環
?? ? ? ?在消息處理機制中,消息都是存放在一個消息隊列中去,而應用程序的主線程就是圍繞這個消息隊列進入一個無限循環的,直到應用程序退出。如果隊列中有消 息,應用程序的主線程就會把它取出來,并分發給相應的Handler進行處理;如果隊列中沒有消息,應用程序的主線程就會進入空閑等待狀態,等待下一個消 息的到來。在Android應用程序中,這個消息循環過程是由Looper類來實現的,它定義在frameworks/base/core/java /android/os/Looper.java文件中,在分析這個類之前,我們先看一下Android應用程序主線程是如何進入到這個消息循環中去的。
?? ? ? ?在Android應用程序進程啟動過程的源代碼分析一 文中,我們分析了Android應用程序進程的啟動過程,Android應用程序進程在啟動的時候,會在進程中加載ActivityThread類,并且 執行這個類的main函數,應用程序的消息循環過程就是在這個main函數里面實現的,我們來看看這個函數的實現,它定義在 frameworks/base/core/java/android/app/ActivityThread.java文件中:
[java] view plaincopy
public?final?class?ActivityThread?{??????......????????public?static?final?void?main(String[]?args)?{??????????......????????????Looper.prepareMainLooper();????????????......????????????ActivityThread?thread?=?new?ActivityThread();??????????thread.attach(false);????????????????????......????????????Looper.loop();????????????......????????????thread.detach();????????????......??????}??}?? ?? ? ? ?這個函數做了兩件事情,一是在主線程中創建了一個ActivityThread實例,二是通過Looper類使主線程進入消息循環中,這里我們只關注后者。
?? ? ? ?首先看Looper.prepareMainLooper函數的實現,這是一個靜態成員函數,定義在frameworks/base/core/java/android/os/Looper.java文件中:
[java] view plaincopy
public?class?Looper?{??????......????????private?static?final?ThreadLocal?sThreadLocal?=?new?ThreadLocal();????????final?MessageQueue?mQueue;????????......????????????public?static?final?void?prepare()?{??????????if?(sThreadLocal.get()?!=?null)?{??????????????throw?new?RuntimeException("Only?one?Looper?may?be?created?per?thread");??????????}??????????sThreadLocal.set(new?Looper());??????}??????????????public?static?final?void?prepareMainLooper()?{??????????prepare();??????????setMainLooper(myLooper());??????????if?(Process.supportsProcesses())?{??????????????myLooper().mQueue.mQuitAllowed?=?false;??????????}??????}????????private?synchronized?static?void?setMainLooper(Looper?looper)?{??????????mMainLooper?=?looper;??????}????????????public?static?final?Looper?myLooper()?{??????????return?(Looper)sThreadLocal.get();??????}????????private?Looper()?{??????????mQueue?=?new?MessageQueue();??????????mRun?=?true;??????????mThread?=?Thread.currentThread();??????}????????......??}?? ?? ? ? ?函數prepareMainLooper做的事情其實就是在線程中創建一個Looper對象,這個Looper對象是存放在sThreadLocal成 員變量里面的,成員變量sThreadLocal的類型為ThreadLocal,表示這是一個線程局部變量,即保證每一個調用了 prepareMainLooper函數的線程里面都有一個獨立的Looper對象。在線程是創建Looper對象的工作是由prepare函數來完成 的,而在創建Looper對象的時候,會同時創建一個消息隊列MessageQueue,保存在Looper的成員變量mQueue中,后續消息就是存放 在這個隊列中去。消息隊列在Android應用程序消息處理機制中最重要的組件,因此,我們看看它的創建過程,即它的構造函數的實現,實現 frameworks/base/core/java/android/os/MessageQueue.java文件中:
[java] view plaincopy
public?class?MessageQueue?{??????......????????private?int?mPtr;???????private?native?void?nativeInit();????????MessageQueue()?{??????????nativeInit();??????}????????......??}?? ?? ? ? ?它的初始化工作都交給JNI方法nativeInit來實現了,這個JNI方法定義在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:
[cpp] view plaincopy
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中,也相應地創建了一個消息隊列NativeMessageQueue,NativeMessageQueue類也是定義在 frameworks/base/core/jni/android_os_MessageQueue.cpp文件中,它的創建過程如下所示:
[cpp] view plaincopy
NativeMessageQueue::NativeMessageQueue()?{??????mLooper?=?Looper::getForThread();??????if?(mLooper?==?NULL)?{??????????mLooper?=?new?Looper(false);??????????Looper::setForThread(mLooper);??????}??}?? ?? ? ? ?它主要就是在內部創建了一個Looper對象,注意,這個Looper對象是實現在JNI層的,它與上面Java層中的Looper是不一樣的,不過它們是對應的,下面我們進一步分析消息循環的過程的時候,讀者就會清楚地了解到它們之間的關系。
?? ? ? ?這個Looper的創建過程也很重要,不過我們暫時放一放,先分析完android_os_MessageQueue_nativeInit函數的執 行,它創建了本地消息隊列NativeMessageQueue對象之后,接著調用 android_os_MessageQueue_setNativeMessageQueue函數來把這個消息隊列對象保存在前面我們在Java層中創 建的MessageQueue對象的mPtr成員變量里面:
[cpp] view plaincopy
static?void?android_os_MessageQueue_setNativeMessageQueue(JNIEnv*?env,?jobject?messageQueueObj,??????????NativeMessageQueue*?nativeMessageQueue)?{??????env->SetIntField(messageQueueObj,?gMessageQueueClassInfo.mPtr,???????????????reinterpret_cast<jint>(nativeMessageQueue));??}?? ?? ? ? ?這里傳進來的參數messageQueueObj即為我們前面在Java層創建的消息隊列對象,而 gMessageQueueClassInfo.mPtr即表示在Java類MessageQueue中,其成員變量mPtr的偏移量,通過這個偏移量, 就可以把這個本地消息隊列對象natvieMessageQueue保存在Java層創建的消息隊列對象的mPtr成員變量中,這是為了后續我們調用 Java層的消息隊列對象的其它成員函數進入到JNI層時,能夠方便地找回它在JNI層所對應的消息隊列對象。
?? ? ? ?我們再回到NativeMessageQueue的構造函數中,看看JNI層的Looper對象的創建過程,即看看它的構造函數是如何實現的,這個Looper類實現在frameworks/base/libs/utils/Looper.cpp文件中:
[cpp] view plaincopy
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??????????mEpollFd?=?epoll_create(EPOLL_SIZE_HINT);??????......????????struct?epoll_event?eventItem;??????memset(&?eventItem,?0,?sizeof(epoll_event));?????eventItem.events?=?EPOLLIN;??????eventItem.data.fd?=?mWakeReadPipeFd;??????result?=?epoll_ctl(mEpollFd,?EPOLL_CTL_ADD,?mWakeReadPipeFd,?&?eventItem);??????......??#else??????......??#endif????????......??}?? ?? ? ? ?這個構造函數做的事情非常重要,它跟我們后面要介紹的應用程序主線程在消息隊列中沒有消息時要進入等待狀態以及當消息隊列有消息時要把應用程序主線程喚醒的這兩個知識點息息相關。它主要就是通過pipe系統調用來創建了一個管道了:
[cpp] view plaincopy
int?wakeFds[2];??int?result?=?pipe(wakeFds);??......????mWakeReadPipeFd?=?wakeFds[0];??mWakeWritePipeFd?=?wakeFds[1];?? ?? ? ? ?管道是Linux系統中的一種進程間通信機制,具體可以參考前面一篇文章Android學習啟動篇推 薦的一本書《Linux內核源代碼情景分析》中的第6章--傳統的Uinx進程間通信。簡單來說,管道就是一個文件,在管道的兩端,分別是兩個打開文件文 件描述符,這兩個打開文件描述符都是對應同一個文件,其中一個是用來讀的,別一個是用來寫的,一般的使用方式就是,一個線程通過讀文件描述符中來讀管道的 內容,當管道沒有內容時,這個線程就會進入等待狀態,而另外一個線程通過寫文件描述符來向管道中寫入內容,寫入內容的時候,如果另一端正有線程正在等待管 道中的內容,那么這個線程就會被喚醒。這個等待和喚醒的操作是如何進行的呢,這就要借助Linux系統中的epoll機制了。?Linux系統中的 epoll機制為處理大批量句柄而作了改進的poll,是Linux下多路復用IO接口select/poll的增強版本,它能顯著減少程序在大量并發連 接中只有少量活躍的情況下的系統CPU利用率。但是這里我們其實只需要監控的IO接口只有mWakeReadPipeFd一個,即前面我們所創建的管道的 讀端,為什么還需要用到epoll呢?有點用牛刀來殺雞的味道。其實不然,這個Looper類是非常強大的,它除了監控內部所創建的管道接口之外,還提供 了addFd接口供外界面調用,外界可以通過這個接口把自己想要監控的IO事件一并加入到這個Looper對象中去,當所有這些被監控的IO接口上面有事 件發生時,就會喚醒相應的線程來處理,不過這里我們只關心剛才所創建的管道的IO事件的發生。
?? ? ? ?要使用Linux系統的epoll機制,首先要通過epoll_create來創建一個epoll專用的文件描述符:
[cpp] view plaincopy
mEpollFd?=?epoll_create(EPOLL_SIZE_HINT);?? ?? ? ? 傳入的參數EPOLL_SIZE_HINT是在這個mEpollFd上能監控的最大文件描述符數。
?? ? ? 接著還要通過epoll_ctl函數來告訴epoll要監控相應的文件描述符的什么事件:
[cpp] view plaincopy
struct?epoll_event?eventItem;??memset(&?eventItem,?0,?sizeof(epoll_event));?eventItem.events?=?EPOLLIN;??eventItem.data.fd?=?mWakeReadPipeFd;??result?=?epoll_ctl(mEpollFd,?EPOLL_CTL_ADD,?mWakeReadPipeFd,?&?eventItem);?? ?? ? ? 這里就是告訴mEpollFd,它要監控mWakeReadPipeFd文件描述符的EPOLLIN事件,即當管道中有內容可讀時,就喚醒當前正在等待管道中的內容的線程。
?? ? ? C++層的這個Looper對象創建好了之后,就返回到JNI層的NativeMessageQueue的構造函數,最后就返回到Java層的消息隊列 MessageQueue的創建過程,這樣,Java層的Looper對象就準備好了。有點復雜,我們先小結一下這一步都做了些什么事情:
?? ? ? A. 在Java層,創建了一個Looper對象,這個Looper對象是用來進入消息循環的,它的內部有一個消息隊列MessageQueue對象mQueue;
?? ? ? B. 在JNI層,創建了一個NativeMessageQueue對象,這個NativeMessageQueue對象保存在Java層的消息隊列對象mQueue的成員變量mPtr中;
?? ? ? C. 在C++層,創建了一個Looper對象,保存在JNI層的NativeMessageQueue對象的成員變量mLooper中,這個對象的作用是,當 Java層的消息隊列中沒有消息時,就使Android應用程序主線程進入等待狀態,而當Java層的消息隊列中來了新的消息后,就喚醒Android應 用程序的主線程來處理這個消息。
?? ? ? 回到ActivityThread類的main函數中,在上面這些工作都準備好之后,就調用Looper類的loop函數進入到消息循環中去了:
[cpp] view plaincopy
public?class?Looper?{??????......????????public?static?final?void?loop()?{??????????Looper?me?=?myLooper();??????????MessageQueue?queue?=?me.mQueue;????????????......????????????while?(true)?{??????????????Message?msg?=?queue.next();?????????????......????????????????if?(msg?!=?null)?{??????????????????if?(msg.target?==?null)?{??????????????????????????????????????????return;??????????????????}????????????????????......????????????????????msg.target.dispatchMessage(msg);????????????????????????????????????......????????????????????msg.recycle();??????????????}??????????}??????}????????......??}?? ?? ? ? ?這里就是進入到消息循環中去了,它不斷地從消息隊列mQueue中去獲取下一個要處理的消息msg,如果消息的target成員變量為null,就表示 要退出消息循環了,否則的話就要調用這個target對象的dispatchMessage成員函數來處理這個消息,這個target對象的類型為 Handler,下面我們分析消息的發送時會看到這個消息對象msg是如設置的。
?? ? ? ?這個函數最關鍵的地方便是從消息隊列中獲取下一個要處理的消息了,即MessageQueue.next函數,它實現frameworks/base/core/java/android/os/MessageQueue.java文件中:
[java] view plaincopy
public?class?MessageQueue?{??????......????????final?Message?next()?{??????????int?pendingIdleHandlerCount?=?-1;?????????int?nextPollTimeoutMillis?=?0;????????????for?(;;)?{??????????????if?(nextPollTimeoutMillis?!=?0)?{??????????????????Binder.flushPendingCommands();??????????????}??????????????nativePollOnce(mPtr,?nextPollTimeoutMillis);????????????????synchronized?(this)?{??????????????????????????????????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?(pendingIdleHandlerCount?<?0)?{??????????????????????pendingIdleHandlerCount?=?mIdleHandlers.size();??????????????????}??????????????????if?(pendingIdleHandlerCount?==?0)?{??????????????????????????????????????????mBlocked?=?true;??????????????????????continue;??????????????????}????????????????????if?(mPendingIdleHandlers?==?null)?{??????????????????????mPendingIdleHandlers?=?new?IdleHandler[Math.max(pendingIdleHandlerCount,?4)];??????????????????}??????????????????mPendingIdleHandlers?=?mIdleHandlers.toArray(mPendingIdleHandlers);??????????????}????????????????????????????????????????for?(int?i?=?0;?i?<?pendingIdleHandlerCount;?i++)?{??????????????????final?IdleHandler?idler?=?mPendingIdleHandlers[i];??????????????????mPendingIdleHandlers[i]?=?null;???????????????????boolean?keep?=?false;??????????????????try?{??????????????????????keep?=?idler.queueIdle();??????????????????}?catch?(Throwable?t)?{??????????????????????Log.wtf("MessageQueue",?"IdleHandler?threw?exception",?t);??????????????????}????????????????????if?(!keep)?{??????????????????????synchronized?(this)?{??????????????????????????mIdleHandlers.remove(idler);??????????????????????}??????????????????}??????????????}????????????????????????????pendingIdleHandlerCount?=?0;????????????????????????????????????????nextPollTimeoutMillis?=?0;??????????}??????}????????......??}?? ?? ? ? ?調用這個函數的時候,有可能會讓線程進入等待狀態。什么情況下,線程會進入等待狀態呢?兩種情況,一是當消息隊列中沒有消息時,它會使線程進入等待狀 態;二是消息隊列中有消息,但是消息指定了執行的時間,而現在還沒有到這個時間,線程也會進入等待狀態。消息隊列中的消息是按時間先后來排序的,后面我們 在分析消息的發送時會看到。
?? ? ? ?執行下面語句是看看當前消息隊列中有沒有消息:
[java] view plaincopy
nativePollOnce(mPtr,?nextPollTimeoutMillis);?? ?? ? ? ?這是一個JNI方法,我們等一下再分析,這里傳入的參數mPtr就是指向前面我們在JNI層創建的NativeMessageQueue對象了,而參數 nextPollTimeoutMillis則表示如果當前消息隊列中沒有消息,它要等待的時候,for循環開始時,傳入的值為0,表示不等待。
?? ? ? ?當前nativePollOnce返回后,就去看看消息隊列中有沒有消息:
[java] view plaincopy
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;??}?? ?? ? ? ?如果消息隊列中有消息,并且當前時候大于等于消息中的執行時間,那么就直接返回這個消息給Looper.loop消息處理,否則的話就要等待到消息的執行時間:
[java] view plaincopy
nextPollTimeoutMillis?=?(int)?Math.min(when?-?now,?Integer.MAX_VALUE);?? ?? ? ? ?如果消息隊列中沒有消息,那就要進入無窮等待狀態直到有新消息了:
[java] view plaincopy
nextPollTimeoutMillis?=?-1;?? ?? ? ? ?-1表示下次調用nativePollOnce時,如果消息中沒有消息,就進入無限等待狀態中去。
?? ? ? ?這里計算出來的等待時間都是在下次調用nativePollOnce時使用的。
?? ? ? ?這里說的等待,是空閑等待,而不是忙等待,因此,在進入空閑等待狀態前,如果應用程序注冊了IdleHandler接口來處理一些事情,那么就會先執行 這里IdleHandler,然后再進入等待狀態。IdlerHandler是定義在MessageQueue的一個內部類:
[java] view plaincopy
public?class?MessageQueue?{??????......????????????public?static?interface?IdleHandler?{??????????????????boolean?queueIdle();??????}????????......??}?? ?? ? ? ?它只有一個成員函數queueIdle,執行這個函數時,如果返回值為false,那么就會從應用程序中移除這個IdleHandler,否則的話就會 在應用程序中繼續維護著這個IdleHandler,下次空閑時仍會再執會這個IdleHandler。MessageQueue提供了 addIdleHandler和removeIdleHandler兩注冊和刪除IdleHandler。
?? ? ? ?回到MessageQueue函數中,它接下來就是在進入等待狀態前,看看有沒有IdleHandler是需要執行的:
[java] view plaincopy
if?(pendingIdleHandlerCount?<?0)?{??????pendingIdleHandlerCount?=?mIdleHandlers.size();??}??if?(pendingIdleHandlerCount?==?0)?{??????????mBlocked?=?true;??????continue;??}????if?(mPendingIdleHandlers?==?null)?{??????mPendingIdleHandlers?=?new?IdleHandler[Math.max(pendingIdleHandlerCount,?4)];??}??mPendingIdleHandlers?=?mIdleHandlers.toArray(mPendingIdleHandlers);?? ?? ? ? ?如果沒有,即pendingIdleHandlerCount等于0,那下面的邏輯就不執行了,通過continue語句直接進入下一次循環,否則就要 把注冊在mIdleHandlers中的IdleHandler取出來,放在mPendingIdleHandlers數組中去。
?? ? ? ?接下來就是執行這些注冊了的IdleHanlder了:
[java] view plaincopy
for?(int?i?=?0;?i?<?pendingIdleHandlerCount;?i++)?{????????final?IdleHandler?idler?=?mPendingIdleHandlers[i];????????mPendingIdleHandlers[i]?=?null;?????????boolean?keep?=?false;????????try?{??????????????keep?=?idler.queueIdle();????????}?catch?(Throwable?t)?{??????????????Log.wtf("MessageQueue",?"IdleHandler?threw?exception",?t);????????}??????????if?(!keep)?{??????????????synchronized?(this)?{??????????????????????mIdleHandlers.remove(idler);??????????????}????????}??}?? ?? ? ? ? 執行完這些IdleHandler之后,線程下次調用nativePollOnce函數時,就不設置超時時間了,因為,很有可能在執行 IdleHandler的時候,已經有新的消息加入到消息隊列中去了,因此,要重置nextPollTimeoutMillis的值:
[java] view plaincopy
nextPollTimeoutMillis?=?0;?? ?? ? ? ?分析完MessageQueue的這個next函數之后,我們就要深入分析一下JNI方法nativePollOnce了,看看它是如何進入等待狀態 的,這個函數定義在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:
[cpp] view plaincopy
static?void?android_os_MessageQueue_nativePollOnce(JNIEnv*?env,?jobject?obj,??????????jint?ptr,?jint?timeoutMillis)?{??????NativeMessageQueue*?nativeMessageQueue?=?reinterpret_cast<NativeMessageQueue*>(ptr);??????nativeMessageQueue->pollOnce(timeoutMillis);??}?? ?? ? ? ?這個函數首先是通過傳進入的參數ptr取回前面在Java層創建MessageQueue對象時在JNI層創建的NatvieMessageQueue對象,然后調用它的pollOnce函數:
[cpp] view plaincopy
void?NativeMessageQueue::pollOnce(int?timeoutMillis)?{??????mLooper->pollOnce(timeoutMillis);??}?? ?? ? ? ?這里將操作轉發給mLooper對象的pollOnce函數處理,這里的mLooper對象是在C++層的對象,它也是在前面在JNI層創建的 NatvieMessageQueue對象時創建的,它的pollOnce函數定義在frameworks/base/libs/utils /Looper.cpp文件中:
[cpp] view plaincopy
int?Looper::pollOnce(int?timeoutMillis,?int*?outFd,?int*?outEvents,?void**?outData)?{??????int?result?=?0;??????for?(;;)?{??????????......????????????if?(result?!=?0)?{??????????????......????????????????return?result;??????????}????????????result?=?pollInner(timeoutMillis);??????}??}?? ?? ? ? ?為了方便討論,我們把這個函數的無關部分都去掉,它主要就是調用pollInner函數來進一步操作,如果pollInner返回值不等于0,這個函數就可以返回了。
?? ? ? ?函數pollInner的定義如下:
[cpp] view plaincopy
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;??}?? ?? ? ? ?這里,首先是調用epoll_wait函數來看看epoll專用文件描述符mEpollFd所監控的文件描述符是否有IO事件發生,它設置監控的超時時間為timeoutMillis:
[cpp] view plaincopy
int?eventCount?=?epoll_wait(mEpollFd,?eventItems,?EPOLL_MAX_EVENTS,?timeoutMillis);?? ?? ? ? ?回憶一下前面的Looper的構造函數,我們在里面設置了要監控mWakeReadPipeFd文件描述符的EPOLLIN事件。
?? ? ? ?當mEpollFd所監控的文件描述符發生了要監控的IO事件后或者監控時間超時后,線程就從epoll_wait返回了,否則線程就會在epoll_wait函數中進入睡眠狀態了。返回后如果eventCount等于0,就說明是超時了:
[cpp] view plaincopy
if?(eventCount?==?0)?{??????......??????result?=?ALOOPER_POLL_TIMEOUT;??????goto?Done;??}?? ?? ? ? 如果eventCount不等于0,就說明發生要監控的事件:
[cpp] view plaincopy
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?{??????????????......??????}??}?? ?? ? ? ?這里我們只關注mWakeReadPipeFd文件描述符上的事件,如果在mWakeReadPipeFd文件描述符上發生了EPOLLIN就說明應用 程序中的消息隊列里面有新的消息需要處理了,接下來它就會先調用awoken函數清空管道中把內容,以便下次再調用pollInner函數時,知道自從上 次處理完消息隊列中的消息后,有沒有新的消息加進來。
?? ? ? ?函數awoken的實現很簡單,它只是把管道中的內容都讀取出來:
[cpp] view plaincopy
void?Looper::awoken()?{??????......????????char?buffer[16];??????ssize_t?nRead;??????do?{??????????nRead?=?read(mWakeReadPipeFd,?buffer,?sizeof(buffer));??????}?while?((nRead?==?-1?&&?errno?==?EINTR)?||?nRead?==?sizeof(buffer));??}?? ?? ? ? ?因為當其它的線程向應用程序的消息隊列加入新的消息時,會向這個管道寫入新的內容來通知應用程序主線程有新的消息需要處理了,下面我們分析消息的發送的時候將會看到。
?? ? ? ?這樣,消息的循環過程就分析完了,這部分邏輯還是比較復雜的,它利用Linux系統中的管道(pipe)進程間通信機制來實現消息的等待和處理,不過,了解了這部分內容之后,下面我們分析消息的發送和處理就簡單多了。
?? ? ? ?2. 消息的發送
?? ? ? ?應用程序的主線程準備就好消息隊列并且進入到消息循環后,其它地方就可以往這個消息隊列中發送消息了。我們繼續以文章開始介紹的Android應用程序啟動過程源代碼分析一文中的應用程序啟動過為例,說明應用程序是如何把消息加入到應用程序的消息隊列中去的。
?? ? ? ?在Android應用程序啟動過程源代碼分析這 篇文章的Step 30中,ActivityManagerService通過調用ApplicationThread類的scheduleLaunchActivity函 數通知應用程序,它可以加載應用程序的默認Activity了,這個函數定義在frameworks/base/core/java/android /app/ActivityThread.java文件中:
[java] view plaincopy
public?final?class?ActivityThread?{????????????......????????????private?final?class?ApplicationThread?extends?ApplicationThreadNative?{????????????????......????????????????????????????????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);????????????}????????????????......????????????}????????????......????}???? ?? ? ? ?這里把相關的參數都封裝成一個ActivityClientRecord對象r,然后調用queueOrSendMessage函數來往應用程序的消息 隊列中加入一個新的消息(H.LAUNCH_ACTIVITY),這個函數定義在frameworks/base/core/java/android /app/ActivityThread.java文件中:
[java] view plaincopy
public?final?class?ActivityThread?{????????????......????????????private?final?class?ApplicationThread?extends?ApplicationThreadNative?{????????????????......????????????????????????????????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函數中,又進一步把上面傳進來的參數封裝成一個Message對象msg,然后通過 mH.sendMessage函數把這個消息對象msg加入到應用程序的消息隊列中去。這里的mH是ActivityThread類的成員變量,它的類型 為H,繼承于Handler類,它定義在frameworks/base/core/java/android/app /ActivityThread.java文件中:
[java] view plaincopy
public?final?class?ActivityThread?{????????????......????????????private?final?class?H?extends?Handler?{????????????????......????????????????public?void?handleMessage(Message?msg)?{????????????????......????????????????switch?(msg.what)?{??????????????????......????????????????}????????????????......????????????}????????????......????}??? ?? ? ? ?這個H類就是通過其成員函數handleMessage函數來處理消息的了,后面我們分析消息的處理過程時會看到。
?? ? ? ?ActivityThread類的這個mH成員變量是什么時候創建的呢?我們前面在分析應用程序的消息循環時,說到當應用程序進程啟動之后,就會加載 ActivityThread類的main函數里面,在這個main函數里面,在通過Looper類進入消息循環之前,會在當前進程中創建一個 ActivityThread實例:
[java] view plaincopy
public?final?class?ActivityThread?{??????......????????public?static?final?void?main(String[]?args)?{??????????......????????????ActivityThread?thread?=?new?ActivityThread();??????????thread.attach(false);????????????......??????}??}?? ?? ? ? ?在創建這個實例的時候,就會同時創建其成員變量mH了:
[java] view plaincopy
public?final?class?ActivityThread?{??????......????????final?H?mH?=?new?H();????????......??}??? ?? ? ? ?前面說過,H類繼承于Handler類,因此,當創建這個H對象時,會調用Handler類的構造函數,這個函數定義在frameworks/base/core/java/android/os/Handler.java文件中:
[java] view plaincopy
public?class?Handler?{??????......????????public?Handler()?{??????????......????????????mLooper?=?Looper.myLooper();??????????......????????????mQueue?=?mLooper.mQueue;??????????......??????}??????????final?MessageQueue?mQueue;??????final?Looper?mLooper;??????......??}?? ?? ? ? ?在Hanlder類的構造函數中,主要就是初始成員變量mLooper和mQueue了。這里的myLooper是Looper類的靜態成員函數,通過 它來獲得一個Looper對象,這個Looper對象就是前面我們在分析消息循環時,在ActivityThread類的main函數中通過 Looper.prepareMainLooper函數創建的。Looper.myLooper函數實現在frameworks/base/core /java/android/os/Looper.java文件中:
[java] view plaincopy
public?class?Looper?{??????......????????public?static?final?Looper?myLooper()?{??????????return?(Looper)sThreadLocal.get();??????}????????......??}?? ?? ? ? ?有了這個Looper對象后,就可以通過Looper.mQueue來訪問應用程序的消息隊列了。
?? ? ? ?有了這個Handler對象mH后,就可以通過它來往應用程序的消息隊列中加入新的消息了。回到前面的queueOrSendMessage函數中,當 它準備好了一個Message對象msg后,就開始調用mH.sendMessage函數來發送消息了,這個函數定義在frameworks/base /core/java/android/os/Handler.java文件中:
[java] view plaincopy
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;??????}????????......??}?? ?? ? ? ?在發送消息時,是可以指定消息的處理時間的,但是通過sendMessage函數發送的消息的處理時間默認就為當前時間,即表示要馬上處理,因此,從 sendMessage函數中調用sendMessageDelayed函數,傳入的時間參數為0,表示這個消息不要延時處理,而在 sendMessageDelayed函數中,則會先獲得當前時間,然后加上消息要延時處理的時間,即得到這個處理這個消息的絕對時間,然后調用 sendMessageAtTime函數來把消息加入到應用程序的消息隊列中去。
?? ? ? ?在sendMessageAtTime函數,首先得到應用程序的消息隊列mQueue,這是在Handler對象構造時初始化好的,前面已經分析過了,接著設置這個消息的目標對象target,即這個消息最終是由誰來處理的:
[java] view plaincopy
msg.target?=?this;?? ?? ? ? ?這里將它賦值為this,即表示這個消息最終由這個Handler對象來處理,即由ActivityThread對象的mH成員變量來處理。
?? ? ? ?函數最后調用queue.enqueueMessage來把這個消息加入到應用程序的消息隊列中去,這個函數實現在frameworks/base/core/java/android/os/MessageQueue.java文件中:
[java] view plaincopy
public?class?MessageQueue?{??????......????????final?boolean?enqueueMessage(Message?msg,?long?when)?{??????????......????????????final?boolean?needWake;??????????synchronized?(this)?{??????????????......????????????????msg.when?=?when;??????????????????????????Message?p?=?mMessages;??????????????if?(p?==?null?||?when?==?0?||?when?<?p.when)?{??????????????????msg.next?=?p;??????????????????mMessages?=?msg;??????????????????needWake?=?mBlocked;?????????????}?else?{??????????????????Message?prev?=?null;??????????????????while?(p?!=?null?&&?p.when?<=?when)?{??????????????????????prev?=?p;??????????????????????p?=?p.next;??????????????????}??????????????????msg.next?=?prev.next;??????????????????prev.next?=?msg;??????????????????needWake?=?false;?????????????}????????????}??????????if?(needWake)?{??????????????nativeWake(mPtr);??????????}??????????return?true;??????}????????......??}?? ?? ? ? ?把消息加入到消息隊列時,分兩種情況,一種當前消息隊列為空時,這時候應用程序的主線程一般就是處于空閑等待狀態了,這時候就要喚醒它,另一種情況是應 用程序的消息隊列不為空,這時候就不需要喚醒應用程序的主線程了,因為這時候它一定是在忙著處于消息隊列中的消息,因此不會處于空閑等待的狀態。
?? ? ? ?第一種情況比較簡單,只要把消息放在消息隊列頭就可以了:
[java] view plaincopy
msg.next?=?p;??mMessages?=?msg;??needWake?=?mBlocked;? ?? ? ? ?第二種情況相對就比較復雜一些了,前面我們說過,當往消息隊列中發送消息時,是可以指定消息的處理時間的,而消息隊列中的消息,就是按照這個時間從小到 大來排序的,因此,當把新的消息加入到消息隊列時,就要根據它的處理時間來找到合適的位置,然后再放進消息隊列中去:
[java] view plaincopy
Message?prev?=?null;??while?(p?!=?null?&&?p.when?<=?when)?{??????prev?=?p;??????p?=?p.next;??}??msg.next?=?prev.next;??prev.next?=?msg;??needWake?=?false;? ?? ? ? ?把消息加入到消息隊列去后,如果應用程序的主線程正處于空閑等待狀態,就需要調用natvieWake函數來喚醒它了,這是一個JNI方法,定義在 frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:
[java] view plaincopy
static?void?android_os_MessageQueue_nativeWake(JNIEnv*?env,?jobject?obj,?jint?ptr)?{??????NativeMessageQueue*?nativeMessageQueue?=?reinterpret_cast<NativeMessageQueue*>(ptr);??????return?nativeMessageQueue->wake();??}?? ?? ? ? ?這個JNI層的NativeMessageQueue對象我們在前面分析消息循環的時候創建好的,保存在Java層的MessageQueue對象的 mPtr成員變量中,這里把它取回來之后,就調用它的wake函數來喚醒應用程序的主線程,這個函數也是定義在frameworks/base/core /jni/android_os_MessageQueue.cpp文件中:
[java] view plaincopy
void?NativeMessageQueue::wake()?{??????mLooper->wake();??}?? ?? ? ? ?這里它又通過成員變量mLooper的wake函數來執行操作,這里的mLooper成員變量是一個C++層實現的Looper對象,它定義在frameworks/base/libs/utils/Looper.cpp文件中:
[java] view plaincopy
void?Looper::wake()?{??????......????????ssize_t?nWrite;??????do?{??????????nWrite?=?write(mWakeWritePipeFd,?"W",?1);??????}?while?(nWrite?==?-1?&&?errno?==?EINTR);????????.......??}?? ?? ? ? ?這個wake函數很簡單,只是通過打開文件描述符mWakeWritePipeFd往管道的寫入一個"W"字符串。其實,往管道寫入什么內容并不重要, 往管道寫入內容的目的是為了喚醒應用程序的主線程。前面我們在分析應用程序的消息循環時說到,當應用程序的消息隊列中沒有消息處理時,應用程序的主線程就 會進入空閑等待狀態,而這個空閑等待狀態就是通過調用這個Looper類的pollInner函數來進入的,具體就是在pollInner函數中調用 epoll_wait函數來等待管道中有內容可讀的。
?? ? ? ?這時候既然管道中有內容可讀了,應用程序的主線程就會從這里的Looper類的pollInner函數返回到JNI層的nativePollOnce函 數,最后返回到Java層中的MessageQueue.next函數中去,這里它就會發現消息隊列中有新的消息需要處理了,于就會處理這個消息。
?? ? ? ?3. 消息的處理
?? ? ? ?前面在分析消息循環時,說到應用程序的主線程是在Looper類的loop成員函數中進行消息循環過程的,這個函數定義在frameworks/base/core/java/android/os/Looper.java文件中:
[java] view plaincopy
public?class?Looper?{??????......????????public?static?final?void?loop()?{??????????Looper?me?=?myLooper();??????????MessageQueue?queue?=?me.mQueue;????????????......????????????while?(true)?{??????????????Message?msg?=?queue.next();?????????????......????????????????if?(msg?!=?null)?{??????????????????if?(msg.target?==?null)?{??????????????????????????????????????????return;??????????????????}????????????????????......????????????????????msg.target.dispatchMessage(msg);????????????????????????????????????......????????????????????msg.recycle();??????????????}??????????}??????}????????......??}?? ?? ? ? ?它從消息隊列中獲得消息對象msg后,就會調用它的target成員變量的dispatchMessage函數來處理這個消息。在前面分析消息的發送時 說過,這個消息對象msg的成員變量target是在發送消息的時候設置好的,一般就通過哪個Handler來發送消息,就通過哪個Handler來處理 消息。
?? ? ? ?我們繼續以前面分析消息的發送時所舉的例子來分析消息的處理過程。前面說到,在Android應用程序啟動過程源代碼分析這 篇文章的Step 30中,ActivityManagerService通過調用ApplicationThread類的scheduleLaunchActivity函 數通知應用程序,它可以加載應用程序的默認Activity了,而ApplicationThread類的scheduleLaunchActivity 函數最終把這個請求封裝成一個消息,然后通過ActivityThread類的成員變量mH來把這個消息加入到應用程序的消息隊列中去。現在要對這個消息 進行處理了,于是就會調用H類的dispatchMessage函數進行處理。
?? ? ? ?H類沒有實現自己的dispatchMessage函數,但是它繼承了父類Handler的dispatchMessage函數,這個函數定義在 frameworks/base/core/java/android/os/ Handler.java文件中:
[java] view plaincopy
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,于是,就會調用Handler類的 handleMessage函數來處理這個消息,由于H類在繼承Handler類時,重寫了handleMessage函數,因此,這里調用的實際上是H 類的handleMessage函數,這個函數定義在frameworks/base/core/java/android/app /ActivityThread.java文件中:
[java] view plaincopy
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;????????????????......????????????????}????????????????......????????????}????????????......????}???? ?? ? ? ? 因為前面在分析消息的發送時所舉的例子中,發送的消息的類型為H.LAUNCH_ACTIVITY,因此,這里就會調用ActivityThread類的handleLaunchActivity函數來真正地處理這個消息了,后面的具體過程就可以參考Android應用程序啟動過程源代碼分析這篇文章了。
?? ? ? ? 至此,我們就從消息循環、消息發送和消息處理三個部分分析完Android應用程序的消息處理機制了,為了更深理解,這里我們對其中的一些要點作一個總結:
?? ? ? ? A. Android應用程序的消息處理機制由消息循環、消息發送和消息處理三個部分組成的。
?? ? ? ? B. Android應用程序的主線程在進入消息循環過程前,會在內部創建一個Linux管道(Pipe),這個管道的作用是使得Android應用程序主線程 在消息隊列為空時可以進入空閑等待狀態,并且使得當應用程序的消息隊列有消息需要處理時喚醒應用程序的主線程。
?? ? ? ? C. Android應用程序的主線程進入空閑等待狀態的方式實際上就是在管道的讀端等待管道中有新的內容可讀,具體來說就是是通過Linux系統的Epoll機制中的epoll_wait函數進行的。
?? ? ? ? D. 當往Android應用程序的消息隊列中加入新的消息時,會同時往管道中的寫端寫入內容,通過這種方式就可以喚醒正在等待消息到來的應用程序主線程。
?? ? ? ? E. 當應用程序主線程在進入空閑等待前,會認為當前線程處理空閑狀態,于是就會調用那些已經注冊了的IdleHandler接口,使得應用程序有機會在空閑的時候處理一些事情。
老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關注!
轉載于:https://www.cnblogs.com/Free-Thinker/p/4142491.html
總結
以上是生活随笔為你收集整理的Android应用程序消息处理机制(Looper、Handler)分析的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。