Android應用程式訊息處理機制(Looper、Handler)分析

yangxi_001發表於2013-11-18

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
  1. public final class ActivityThread {  
  2.     ......  
  3.   
  4.     public static final void main(String[] args) {  
  5.         ......  
  6.   
  7.         Looper.prepareMainLooper();  
  8.   
  9.         ......  
  10.   
  11.         ActivityThread thread = new ActivityThread();  
  12.         thread.attach(false);  
  13.           
  14.         ......  
  15.   
  16.         Looper.loop();  
  17.   
  18.         ......  
  19.   
  20.         thread.detach();  
  21.   
  22.         ......  
  23.     }  
  24. }  
        這個函式做了兩件事情,一是在主執行緒中建立了一個ActivityThread例項,二是通過Looper類使主執行緒進入訊息迴圈中,這裡我們只關注後者。

        首先看Looper.prepareMainLooper函式的實現,這是一個靜態成員函式,定義在frameworks/base/core/java/android/os/Looper.java檔案中:

[java] view plaincopy
  1. public class Looper {  
  2.     ......  
  3.   
  4.     private static final ThreadLocal sThreadLocal = new ThreadLocal();  
  5.   
  6.     final MessageQueue mQueue;  
  7.   
  8.     ......  
  9.   
  10.     /** Initialize the current thread as a looper. 
  11.     * This gives you a chance to create handlers that then reference 
  12.     * this looper, before actually starting the loop. Be sure to call 
  13.     * {@link #loop()} after calling this method, and end it by calling 
  14.     * {@link #quit()}. 
  15.     */  
  16.     public static final void prepare() {  
  17.         if (sThreadLocal.get() != null) {  
  18.             throw new RuntimeException("Only one Looper may be created per thread");  
  19.         }  
  20.         sThreadLocal.set(new Looper());  
  21.     }  
  22.   
  23.     /** Initialize the current thread as a looper, marking it as an application's main  
  24.     *  looper. The main looper for your application is created by the Android environment, 
  25.     *  so you should never need to call this function yourself. 
  26.     * {@link #prepare()} 
  27.     */  
  28.   
  29.     public static final void prepareMainLooper() {  
  30.         prepare();  
  31.         setMainLooper(myLooper());  
  32.         if (Process.supportsProcesses()) {  
  33.             myLooper().mQueue.mQuitAllowed = false;  
  34.         }  
  35.     }  
  36.   
  37.     private synchronized static void setMainLooper(Looper looper) {  
  38.         mMainLooper = looper;  
  39.     }  
  40.   
  41.     /** 
  42.     * Return the Looper object associated with the current thread.  Returns 
  43.     * null if the calling thread is not associated with a Looper. 
  44.     */  
  45.     public static final Looper myLooper() {  
  46.         return (Looper)sThreadLocal.get();  
  47.     }  
  48.   
  49.     private Looper() {  
  50.         mQueue = new MessageQueue();  
  51.         mRun = true;  
  52.         mThread = Thread.currentThread();  
  53.     }  
  54.   
  55.     ......  
  56. }  
        函式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
  1. public class MessageQueue {  
  2.     ......  
  3.   
  4.     private int mPtr; // used by native code  
  5.   
  6.     private native void nativeInit();  
  7.   
  8.     MessageQueue() {  
  9.         nativeInit();  
  10.     }  
  11.   
  12.     ......  
  13. }  
        它的初始化工作都交給JNI方法nativeInit來實現了,這個JNI方法定義在frameworks/base/core/jni/android_os_MessageQueue.cpp檔案中:

  1. static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {  
  2.     NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();  
  3.     if (! nativeMessageQueue) {  
  4.         jniThrowRuntimeException(env, "Unable to allocate native queue");  
  5.         return;  
  6.     }  
  7.   
  8.     android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);  
  9. }  
        在JNI中,也相應地建立了一個訊息佇列NativeMessageQueue,NativeMessageQueue類也是定義在frameworks/base/core/jni/android_os_MessageQueue.cpp檔案中,它的建立過程如下所示:

  1. NativeMessageQueue::NativeMessageQueue() {  
  2.     mLooper = Looper::getForThread();  
  3.     if (mLooper == NULL) {  
  4.         mLooper = new Looper(false);  
  5.         Looper::setForThread(mLooper);  
  6.     }  
  7. }  
        它主要就是在內部建立了一個Looper物件,注意,這個Looper物件是實現在JNI層的,它與上面Java層中的Looper是不一樣的,不過它們是對應的,下面我們進一步分析訊息迴圈的過程的時候,讀者就會清楚地瞭解到它們之間的關係。

        這個Looper的建立過程也很重要,不過我們暫時放一放,先分析完android_os_MessageQueue_nativeInit函式的執行,它建立了本地訊息佇列NativeMessageQueue物件之後,接著呼叫android_os_MessageQueue_setNativeMessageQueue函式來把這個訊息佇列物件儲存在前面我們在Java層中建立的MessageQueue物件的mPtr成員變數裡面:

  1. static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj,  
  2.         NativeMessageQueue* nativeMessageQueue) {  
  3.     env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr,  
  4.              reinterpret_cast<jint>(nativeMessageQueue));  
  5. }  
        這裡傳進來的引數messageQueueObj即為我們前面在Java層建立的訊息佇列物件,而gMessageQueueClassInfo.mPtr即表示在Java類MessageQueue中,其成員變數mPtr的偏移量,通過這個偏移量,就可以把這個本地訊息佇列物件natvieMessageQueue儲存在Java層建立的訊息佇列物件的mPtr成員變數中,這是為了後續我們呼叫Java層的訊息佇列物件的其它成員函式進入到JNI層時,能夠方便地找回它在JNI層所對應的訊息佇列物件。

        我們再回到NativeMessageQueue的建構函式中,看看JNI層的Looper物件的建立過程,即看看它的建構函式是如何實現的,這個Looper類實現在frameworks/base/libs/utils/Looper.cpp檔案中:

  1. Looper::Looper(bool allowNonCallbacks) :  
  2.     mAllowNonCallbacks(allowNonCallbacks),  
  3.     mResponseIndex(0) {  
  4.     int wakeFds[2];  
  5.     int result = pipe(wakeFds);  
  6.     ......  
  7.   
  8.     mWakeReadPipeFd = wakeFds[0];  
  9.     mWakeWritePipeFd = wakeFds[1];  
  10.   
  11.     ......  
  12.   
  13. #ifdef LOOPER_USES_EPOLL  
  14.     // Allocate the epoll instance and register the wake pipe.  
  15.     mEpollFd = epoll_create(EPOLL_SIZE_HINT);  
  16.     ......  
  17.   
  18.     struct epoll_event eventItem;  
  19.     memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union  
  20.     eventItem.events = EPOLLIN;  
  21.     eventItem.data.fd = mWakeReadPipeFd;  
  22.     result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);  
  23.     ......  
  24. #else  
  25.     ......  
  26. #endif  
  27.   
  28.     ......  
  29. }  
        這個建構函式做的事情非常重要,它跟我們後面要介紹的應用程式主執行緒在訊息佇列中沒有訊息時要進入等待狀態以及當訊息佇列有訊息時要把應用程式主執行緒喚醒的這兩個知識點息息相關。它主要就是通過pipe系統呼叫來建立了一個管道了:

  1. int wakeFds[2];  
  2. int result = pipe(wakeFds);  
  3. ......  
  4.   
  5. mWakeReadPipeFd = wakeFds[0];  
  6. 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專用的檔案描述符:

  1. mEpollFd = epoll_create(EPOLL_SIZE_HINT);  
       傳入的引數EPOLL_SIZE_HINT是在這個mEpollFd上能監控的最大檔案描述符數。

       接著還要通過epoll_ctl函式來告訴epoll要監控相應的檔案描述符的什麼事件:

  1. struct epoll_event eventItem;  
  2. memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union  
  3. eventItem.events = EPOLLIN;  
  4. eventItem.data.fd = mWakeReadPipeFd;  
  5. 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函式進入到訊息迴圈中去了:

  1. public class Looper {  
  2.     ......  
  3.   
  4.     public static final void loop() {  
  5.         Looper me = myLooper();  
  6.         MessageQueue queue = me.mQueue;  
  7.   
  8.         ......  
  9.   
  10.         while (true) {  
  11.             Message msg = queue.next(); // might block  
  12.             ......  
  13.   
  14.             if (msg != null) {  
  15.                 if (msg.target == null) {  
  16.                     // No target is a magic identifier for the quit message.  
  17.                     return;  
  18.                 }  
  19.   
  20.                 ......  
  21.   
  22.                 msg.target.dispatchMessage(msg);  
  23.                   
  24.                 ......  
  25.   
  26.                 msg.recycle();  
  27.             }  
  28.         }  
  29.     }  
  30.   
  31.     ......  
  32. }  
        這裡就是進入到訊息迴圈中去了,它不斷地從訊息佇列mQueue中去獲取下一個要處理的訊息msg,如果訊息的target成員變數為null,就表示要退出訊息迴圈了,否則的話就要呼叫這個target物件的dispatchMessage成員函式來處理這個訊息,這個target物件的型別為Handler,下面我們分析訊息的傳送時會看到這個訊息物件msg是如設定的。

        這個函式最關鍵的地方便是從訊息佇列中獲取下一個要處理的訊息了,即MessageQueue.next函式,它實現frameworks/base/core/java/android/os/MessageQueue.java檔案中:

[java] view plaincopy
  1. public class MessageQueue {  
  2.     ......  
  3.   
  4.     final Message next() {  
  5.         int pendingIdleHandlerCount = -1// -1 only during first iteration  
  6.         int nextPollTimeoutMillis = 0;  
  7.   
  8.         for (;;) {  
  9.             if (nextPollTimeoutMillis != 0) {  
  10.                 Binder.flushPendingCommands();  
  11.             }  
  12.             nativePollOnce(mPtr, nextPollTimeoutMillis);  
  13.   
  14.             synchronized (this) {  
  15.                 // Try to retrieve the next message.  Return if found.  
  16.                 final long now = SystemClock.uptimeMillis();  
  17.                 final Message msg = mMessages;  
  18.                 if (msg != null) {  
  19.                     final long when = msg.when;  
  20.                     if (now >= when) {  
  21.                         mBlocked = false;  
  22.                         mMessages = msg.next;  
  23.                         msg.next = null;  
  24.                         if (Config.LOGV) Log.v("MessageQueue""Returning message: " + msg);  
  25.                         return msg;  
  26.                     } else {  
  27.                         nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);  
  28.                     }  
  29.                 } else {  
  30.                     nextPollTimeoutMillis = -1;  
  31.                 }  
  32.   
  33.                 // If first time, then get the number of idlers to run.  
  34.                 if (pendingIdleHandlerCount < 0) {  
  35.                     pendingIdleHandlerCount = mIdleHandlers.size();  
  36.                 }  
  37.                 if (pendingIdleHandlerCount == 0) {  
  38.                     // No idle handlers to run.  Loop and wait some more.  
  39.                     mBlocked = true;  
  40.                     continue;  
  41.                 }  
  42.   
  43.                 if (mPendingIdleHandlers == null) {  
  44.                     mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];  
  45.                 }  
  46.                 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);  
  47.             }  
  48.   
  49.             // Run the idle handlers.  
  50.             // We only ever reach this code block during the first iteration.  
  51.             for (int i = 0; i < pendingIdleHandlerCount; i++) {  
  52.                 final IdleHandler idler = mPendingIdleHandlers[i];  
  53.                 mPendingIdleHandlers[i] = null// release the reference to the handler  
  54.   
  55.                 boolean keep = false;  
  56.                 try {  
  57.                     keep = idler.queueIdle();  
  58.                 } catch (Throwable t) {  
  59.                     Log.wtf("MessageQueue""IdleHandler threw exception", t);  
  60.                 }  
  61.   
  62.                 if (!keep) {  
  63.                     synchronized (this) {  
  64.                         mIdleHandlers.remove(idler);  
  65.                     }  
  66.                 }  
  67.             }  
  68.   
  69.             // Reset the idle handler count to 0 so we do not run them again.  
  70.             pendingIdleHandlerCount = 0;  
  71.   
  72.             // While calling an idle handler, a new message could have been delivered  
  73.             // so go back and look again for a pending message without waiting.  
  74.             nextPollTimeoutMillis = 0;  
  75.         }  
  76.     }  
  77.   
  78.     ......  
  79. }  
        呼叫這個函式的時候,有可能會讓執行緒進入等待狀態。什麼情況下,執行緒會進入等待狀態呢?兩種情況,一是當訊息佇列中沒有訊息時,它會使執行緒進入等待狀態;二是訊息佇列中有訊息,但是訊息指定了執行的時間,而現在還沒有到這個時間,執行緒也會進入等待狀態。訊息佇列中的訊息是按時間先後來排序的,後面我們在分析訊息的傳送時會看到。

        執行下面語句是看看當前訊息佇列中有沒有訊息:

[java] view plaincopy
  1. nativePollOnce(mPtr, nextPollTimeoutMillis);  
        這是一個JNI方法,我們等一下再分析,這裡傳入的引數mPtr就是指向前面我們在JNI層建立的NativeMessageQueue物件了,而引數nextPollTimeoutMillis則表示如果當前訊息佇列中沒有訊息,它要等待的時候,for迴圈開始時,傳入的值為0,表示不等待。

        當前nativePollOnce返回後,就去看看訊息佇列中有沒有訊息:

[java] view plaincopy
  1. final Message msg = mMessages;  
  2. if (msg != null) {  
  3.     final long when = msg.when;  
  4.     if (now >= when) {  
  5.         mBlocked = false;  
  6.         mMessages = msg.next;  
  7.         msg.next = null;  
  8.         if (Config.LOGV) Log.v("MessageQueue""Returning message: " + msg);  
  9.         return msg;  
  10.     } else {  
  11.         nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);  
  12.     }  
  13. else {  
  14.     nextPollTimeoutMillis = -1;  
  15. }  
        如果訊息佇列中有訊息,並且當前時候大於等於訊息中的執行時間,那麼就直接返回這個訊息給Looper.loop訊息處理,否則的話就要等待到訊息的執行時間:

[java] view plaincopy
  1. nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);  
        如果訊息佇列中沒有訊息,那就要進入無窮等待狀態直到有新訊息了:

[java] view plaincopy
  1. nextPollTimeoutMillis = -1;  
        -1表示下次呼叫nativePollOnce時,如果訊息中沒有訊息,就進入無限等待狀態中去。

        這裡計算出來的等待時間都是在下次呼叫nativePollOnce時使用的。

        這裡說的等待,是空閒等待,而不是忙等待,因此,在進入空閒等待狀態前,如果應用程式註冊了IdleHandler介面來處理一些事情,那麼就會先執行這裡IdleHandler,然後再進入等待狀態。IdlerHandler是定義在MessageQueue的一個內部類:

[java] view plaincopy
  1. public class MessageQueue {  
  2.     ......  
  3.   
  4.     /** 
  5.     * Callback interface for discovering when a thread is going to block 
  6.     * waiting for more messages. 
  7.     */  
  8.     public static interface IdleHandler {  
  9.         /** 
  10.         * Called when the message queue has run out of messages and will now 
  11.         * wait for more.  Return true to keep your idle handler active, false 
  12.         * to have it removed.  This may be called if there are still messages 
  13.         * pending in the queue, but they are all scheduled to be dispatched 
  14.         * after the current time. 
  15.         */  
  16.         boolean queueIdle();  
  17.     }  
  18.   
  19.     ......  
  20. }  
        它只有一個成員函式queueIdle,執行這個函式時,如果返回值為false,那麼就會從應用程式中移除這個IdleHandler,否則的話就會在應用程式中繼續維護著這個IdleHandler,下次空閒時仍會再執會這個IdleHandler。MessageQueue提供了addIdleHandler和removeIdleHandler兩註冊和刪除IdleHandler。

        回到MessageQueue函式中,它接下來就是在進入等待狀態前,看看有沒有IdleHandler是需要執行的:

[java] view plaincopy
  1. // If first time, then get the number of idlers to run.  
  2. if (pendingIdleHandlerCount < 0) {  
  3.     pendingIdleHandlerCount = mIdleHandlers.size();  
  4. }  
  5. if (pendingIdleHandlerCount == 0) {  
  6.     // No idle handlers to run.  Loop and wait some more.  
  7.     mBlocked = true;  
  8.     continue;  
  9. }  
  10.   
  11. if (mPendingIdleHandlers == null) {  
  12.     mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];  
  13. }  
  14. mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);  
        如果沒有,即pendingIdleHandlerCount等於0,那下面的邏輯就不執行了,通過continue語句直接進入下一次迴圈,否則就要把註冊在mIdleHandlers中的IdleHandler取出來,放在mPendingIdleHandlers陣列中去。

        接下來就是執行這些註冊了的IdleHanlder了:

[java] view plaincopy
  1. // Run the idle handlers.  
  2. // We only ever reach this code block during the first iteration.  
  3. for (int i = 0; i < pendingIdleHandlerCount; i++) {  
  4.       final IdleHandler idler = mPendingIdleHandlers[i];  
  5.       mPendingIdleHandlers[i] = null// release the reference to the handler  
  6.   
  7.       boolean keep = false;  
  8.       try {  
  9.             keep = idler.queueIdle();  
  10.       } catch (Throwable t) {  
  11.             Log.wtf("MessageQueue""IdleHandler threw exception", t);  
  12.       }  
  13.   
  14.       if (!keep) {  
  15.             synchronized (this) {  
  16.                     mIdleHandlers.remove(idler);  
  17.             }  
  18.       }  
  19. }  
         執行完這些IdleHandler之後,執行緒下次呼叫nativePollOnce函式時,就不設定超時時間了,因為,很有可能在執行IdleHandler的時候,已經有新的訊息加入到訊息佇列中去了,因此,要重置nextPollTimeoutMillis的值:

[java] view plaincopy
  1. // While calling an idle handler, a new message could have been delivered  
  2. // so go back and look again for a pending message without waiting.  
  3. nextPollTimeoutMillis = 0;  
        分析完MessageQueue的這個next函式之後,我們就要深入分析一下JNI方法nativePollOnce了,看看它是如何進入等待狀態的,這個函式定義在frameworks/base/core/jni/android_os_MessageQueue.cpp檔案中:

  1. static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,  
  2.         jint ptr, jint timeoutMillis) {  
  3.     NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);  
  4.     nativeMessageQueue->pollOnce(timeoutMillis);  
  5. }  
        這個函式首先是通過傳進入的引數ptr取回前面在Java層建立MessageQueue物件時在JNI層建立的NatvieMessageQueue物件,然後呼叫它的pollOnce函式:

  1. void NativeMessageQueue::pollOnce(int timeoutMillis) {  
  2.     mLooper->pollOnce(timeoutMillis);  
  3. }  
        這裡將操作轉發給mLooper物件的pollOnce函式處理,這裡的mLooper物件是在C++層的物件,它也是在前面在JNI層建立的NatvieMessageQueue物件時建立的,它的pollOnce函式定義在frameworks/base/libs/utils/Looper.cpp檔案中:

  1. int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {  
  2.     int result = 0;  
  3.     for (;;) {  
  4.         ......  
  5.   
  6.         if (result != 0) {  
  7.             ......  
  8.   
  9.             return result;  
  10.         }  
  11.   
  12.         result = pollInner(timeoutMillis);  
  13.     }  
  14. }  
        為了方便討論,我們把這個函式的無關部分都去掉,它主要就是呼叫pollInner函式來進一步操作,如果pollInner返回值不等於0,這個函式就可以返回了。

        函式pollInner的定義如下:

  1. int Looper::pollInner(int timeoutMillis) {  
  2.     ......  
  3.   
  4.     int result = ALOOPER_POLL_WAKE;  
  5.   
  6.     ......  
  7.   
  8. #ifdef LOOPER_USES_EPOLL  
  9.     struct epoll_event eventItems[EPOLL_MAX_EVENTS];  
  10.     int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);  
  11.     bool acquiredLock = false;  
  12. #else  
  13.     ......  
  14. #endif  
  15.   
  16.     if (eventCount < 0) {  
  17.         if (errno == EINTR) {  
  18.             goto Done;  
  19.         }  
  20.   
  21.         LOGW("Poll failed with an unexpected error, errno=%d", errno);  
  22.         result = ALOOPER_POLL_ERROR;  
  23.         goto Done;  
  24.     }  
  25.   
  26.     if (eventCount == 0) {  
  27.         ......  
  28.         result = ALOOPER_POLL_TIMEOUT;  
  29.         goto Done;  
  30.     }  
  31.   
  32.     ......  
  33.   
  34. #ifdef LOOPER_USES_EPOLL  
  35.     for (int i = 0; i < eventCount; i++) {  
  36.         int fd = eventItems[i].data.fd;  
  37.         uint32_t epollEvents = eventItems[i].events;  
  38.         if (fd == mWakeReadPipeFd) {  
  39.             if (epollEvents & EPOLLIN) {  
  40.                 awoken();  
  41.             } else {  
  42.                 LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);  
  43.             }  
  44.         } else {  
  45.             ......  
  46.         }  
  47.     }  
  48.     if (acquiredLock) {  
  49.         mLock.unlock();  
  50.     }  
  51. Done: ;  
  52. #else  
  53.     ......  
  54. #endif  
  55.   
  56.     ......  
  57.   
  58.     return result;  
  59. }  
        這裡,首先是呼叫epoll_wait函式來看看epoll專用檔案描述符mEpollFd所監控的檔案描述符是否有IO事件發生,它設定監控的超時時間為timeoutMillis:

  1. int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);  
        回憶一下前面的Looper的建構函式,我們在裡面設定了要監控mWakeReadPipeFd檔案描述符的EPOLLIN事件。

        當mEpollFd所監控的檔案描述符發生了要監控的IO事件後或者監控時間超時後,執行緒就從epoll_wait返回了,否則執行緒就會在epoll_wait函式中進入睡眠狀態了。返回後如果eventCount等於0,就說明是超時了:

  1. if (eventCount == 0) {  
  2.     ......  
  3.     result = ALOOPER_POLL_TIMEOUT;  
  4.     goto Done;  
  5. }  
       如果eventCount不等於0,就說明發生要監控的事件:

  1. for (int i = 0; i < eventCount; i++) {  
  2.     int fd = eventItems[i].data.fd;  
  3.     uint32_t epollEvents = eventItems[i].events;  
  4.     if (fd == mWakeReadPipeFd) {  
  5.         if (epollEvents & EPOLLIN) {  
  6.             awoken();  
  7.         } else {  
  8.             LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);  
  9.         }  
  10.     } else {  
  11.             ......  
  12.     }  
  13. }  
        這裡我們只關注mWakeReadPipeFd檔案描述符上的事件,如果在mWakeReadPipeFd檔案描述符上發生了EPOLLIN就說明應用程式中的訊息佇列裡面有新的訊息需要處理了,接下來它就會先呼叫awoken函式清空管道中把內容,以便下次再呼叫pollInner函式時,知道自從上次處理完訊息佇列中的訊息後,有沒有新的訊息加進來。

        函式awoken的實現很簡單,它只是把管道中的內容都讀取出來:

  1. void Looper::awoken() {  
  2.     ......  
  3.   
  4.     char buffer[16];  
  5.     ssize_t nRead;  
  6.     do {  
  7.         nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));  
  8.     } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));  
  9. }  
        因為當其它的執行緒嚮應用程式的訊息佇列加入新的訊息時,會向這個管道寫入新的內容來通知應用程式主執行緒有新的訊息需要處理了,下面我們分析訊息的傳送的時候將會看到。

        這樣,訊息的迴圈過程就分析完了,這部分邏輯還是比較複雜的,它利用Linux系統中的管道(pipe)程式間通訊機制來實現訊息的等待和處理,不過,瞭解了這部分內容之後,下面我們分析訊息的傳送和處理就簡單多了。

        2. 訊息的傳送
        應用程式的主執行緒準備就好訊息佇列並且進入到訊息迴圈後,其它地方就可以往這個訊息佇列中傳送訊息了。我們繼續以文章開始介紹的Android應用程式啟動過程原始碼分析一文中的應用程式啟動過為例,說明應用程式是如何把訊息加入到應用程式的訊息佇列中去的。

        在Android應用程式啟動過程原始碼分析這篇文章的Step 30中,ActivityManagerService通過呼叫ApplicationThread類的scheduleLaunchActivity函式通知應用程式,它可以載入應用程式的預設Activity了,這個函式定義在frameworks/base/core/java/android/app/ActivityThread.java檔案中:

[java] view plaincopy
  1. public final class ActivityThread {    
  2.     
  3.     ......    
  4.     
  5.     private final class ApplicationThread extends ApplicationThreadNative {    
  6.     
  7.         ......    
  8.     
  9.         // we use token to identify this activity without having to send the    
  10.         // activity itself back to the activity manager. (matters more with ipc)    
  11.         public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,    
  12.                 ActivityInfo info, Bundle state, List<ResultInfo> pendingResults,    
  13.                 List<Intent> pendingNewIntents, boolean notResumed, boolean isForward) {    
  14.             ActivityClientRecord r = new ActivityClientRecord();    
  15.     
  16.             r.token = token;    
  17.             r.ident = ident;    
  18.             r.intent = intent;    
  19.             r.activityInfo = info;    
  20.             r.state = state;    
  21.     
  22.             r.pendingResults = pendingResults;    
  23.             r.pendingIntents = pendingNewIntents;    
  24.     
  25.             r.startsNotResumed = notResumed;    
  26.             r.isForward = isForward;    
  27.     
  28.             queueOrSendMessage(H.LAUNCH_ACTIVITY, r);    
  29.         }    
  30.     
  31.         ......    
  32.     
  33.     }    
  34.     
  35.     ......    
  36. }    
        這裡把相關的引數都封裝成一個ActivityClientRecord物件r,然後呼叫queueOrSendMessage函式來往應用程式的訊息佇列中加入一個新的訊息(H.LAUNCH_ACTIVITY),這個函式定義在frameworks/base/core/java/android/app/ActivityThread.java檔案中:

[java] view plaincopy
  1. public final class ActivityThread {    
  2.     
  3.     ......    
  4.     
  5.     private final class ApplicationThread extends ApplicationThreadNative {    
  6.     
  7.         ......    
  8.     
  9.         // if the thread hasn't started yet, we don't have the handler, so just    
  10.         // save the messages until we're ready.    
  11.         private final void queueOrSendMessage(int what, Object obj) {    
  12.             queueOrSendMessage(what, obj, 00);    
  13.         }    
  14.     
  15.         ......    
  16.     
  17.         private final void queueOrSendMessage(int what, Object obj, int arg1, int arg2) {    
  18.             synchronized (this) {    
  19.                 ......    
  20.                 Message msg = Message.obtain();    
  21.                 msg.what = what;    
  22.                 msg.obj = obj;    
  23.                 msg.arg1 = arg1;    
  24.                 msg.arg2 = arg2;    
  25.                 mH.sendMessage(msg);    
  26.             }    
  27.         }    
  28.     
  29.         ......    
  30.     
  31.     }    
  32.     
  33.     ......    
  34. }    
        在queueOrSendMessage函式中,又進一步把上面傳進來的引數封裝成一個Message物件msg,然後通過mH.sendMessage函式把這個訊息物件msg加入到應用程式的訊息佇列中去。這裡的mH是ActivityThread類的成員變數,它的型別為H,繼承於Handler類,它定義在frameworks/base/core/java/android/app/ActivityThread.java檔案中:

[java] view plaincopy
  1. public final class ActivityThread {    
  2.     
  3.     ......    
  4.     
  5.     private final class H extends Handler {    
  6.     
  7.         ......    
  8.     
  9.         public void handleMessage(Message msg) {    
  10.             ......    
  11.             switch (msg.what) {      
  12.             ......    
  13.             }    
  14.     
  15.         ......    
  16.     
  17.     }    
  18.     
  19.     ......    
  20. }   

        這個H類就是通過其成員函式handleMessage函式來處理訊息的了,後面我們分析訊息的處理過程時會看到。
        ActivityThread類的這個mH成員變數是什麼時候建立的呢?我們前面在分析應用程式的訊息迴圈時,說到當應用程式程式啟動之後,就會載入ActivityThread類的main函式裡面,在這個main函式裡面,在通過Looper類進入訊息迴圈之前,會在當前程式中建立一個ActivityThread例項:

[java] view plaincopy
  1. public final class ActivityThread {  
  2.     ......  
  3.   
  4.     public static final void main(String[] args) {  
  5.         ......  
  6.   
  7.         ActivityThread thread = new ActivityThread();  
  8.         thread.attach(false);  
  9.   
  10.         ......  
  11.     }  
  12. }  
        在建立這個例項的時候,就會同時建立其成員變數mH了:

[java] view plaincopy
  1. public final class ActivityThread {  
  2.     ......  
  3.   
  4.     final H mH = new H();  
  5.   
  6.     ......  
  7. }   
        前面說過,H類繼承於Handler類,因此,當建立這個H物件時,會呼叫Handler類的建構函式,這個函式定義在frameworks/base/core/java/android/os/Handler.java檔案中:

[java] view plaincopy
  1. public class Handler {  
  2.     ......  
  3.   
  4.     public Handler() {  
  5.         ......  
  6.   
  7.         mLooper = Looper.myLooper();  
  8.         ......  
  9.   
  10.         mQueue = mLooper.mQueue;  
  11.         ......  
  12.     }  
  13.   
  14.   
  15.     final MessageQueue mQueue;  
  16.     final Looper mLooper;  
  17.     ......  
  18. }  
        在Hanlder類的建構函式中,主要就是初始成員變數mLooper和mQueue了。這裡的myLooper是Looper類的靜態成員函式,通過它來獲得一個Looper物件,這個Looper物件就是前面我們在分析訊息迴圈時,在ActivityThread類的main函式中通過Looper.prepareMainLooper函式建立的。Looper.myLooper函式實現在frameworks/base/core/java/android/os/Looper.java檔案中:

[java] view plaincopy
  1. public class Looper {  
  2.     ......  
  3.   
  4.     public static final Looper myLooper() {  
  5.         return (Looper)sThreadLocal.get();  
  6.     }  
  7.   
  8.     ......  
  9. }  
        有了這個Looper物件後,就可以通過Looper.mQueue來訪問應用程式的訊息佇列了。

        有了這個Handler物件mH後,就可以通過它來往應用程式的訊息佇列中加入新的訊息了。回到前面的queueOrSendMessage函式中,當它準備好了一個Message物件msg後,就開始呼叫mH.sendMessage函式來傳送訊息了,這個函式定義在frameworks/base/core/java/android/os/Handler.java檔案中:

[java] view plaincopy
  1. public class Handler {  
  2.     ......  
  3.   
  4.     public final boolean sendMessage(Message msg)  
  5.     {  
  6.         return sendMessageDelayed(msg, 0);  
  7.     }  
  8.   
  9.     public final boolean sendMessageDelayed(Message msg, long delayMillis)  
  10.     {  
  11.         if (delayMillis < 0) {  
  12.             delayMillis = 0;  
  13.         }  
  14.         return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
  15.     }  
  16.   
  17.     public boolean sendMessageAtTime(Message msg, long uptimeMillis)  
  18.     {  
  19.         boolean sent = false;  
  20.         MessageQueue queue = mQueue;  
  21.         if (queue != null) {  
  22.             msg.target = this;  
  23.             sent = queue.enqueueMessage(msg, uptimeMillis);  
  24.         }  
  25.         else {  
  26.             ......  
  27.         }  
  28.         return sent;  
  29.     }  
  30.   
  31.     ......  
  32. }  
        在傳送訊息時,是可以指定訊息的處理時間的,但是通過sendMessage函式傳送的訊息的處理時間預設就為當前時間,即表示要馬上處理,因此,從sendMessage函式中呼叫sendMessageDelayed函式,傳入的時間引數為0,表示這個訊息不要延時處理,而在sendMessageDelayed函式中,則會先獲得當前時間,然後加上訊息要延時處理的時間,即得到這個處理這個訊息的絕對時間,然後呼叫sendMessageAtTime函式來把訊息加入到應用程式的訊息佇列中去。

        在sendMessageAtTime函式,首先得到應用程式的訊息佇列mQueue,這是在Handler物件構造時初始化好的,前面已經分析過了,接著設定這個訊息的目標物件target,即這個訊息最終是由誰來處理的:

[java] view plaincopy
  1. msg.target = this;  
        這裡將它賦值為this,即表示這個訊息最終由這個Handler物件來處理,即由ActivityThread物件的mH成員變數來處理。

        函式最後呼叫queue.enqueueMessage來把這個訊息加入到應用程式的訊息佇列中去,這個函式實現在frameworks/base/core/java/android/os/MessageQueue.java檔案中:

[java] view plaincopy
  1. public class MessageQueue {  
  2.     ......  
  3.   
  4.     final boolean enqueueMessage(Message msg, long when) {  
  5.         ......  
  6.   
  7.         final boolean needWake;  
  8.         synchronized (this) {  
  9.             ......  
  10.   
  11.             msg.when = when;  
  12.             //Log.d("MessageQueue", "Enqueing: " + msg);  
  13.             Message p = mMessages;  
  14.             if (p == null || when == 0 || when < p.when) {  
  15.                 msg.next = p;  
  16.                 mMessages = msg;  
  17.                 needWake = mBlocked; // new head, might need to wake up  
  18.             } else {  
  19.                 Message prev = null;  
  20.                 while (p != null && p.when <= when) {  
  21.                     prev = p;  
  22.                     p = p.next;  
  23.                 }  
  24.                 msg.next = prev.next;  
  25.                 prev.next = msg;  
  26.                 needWake = false// still waiting on head, no need to wake up  
  27.             }  
  28.   
  29.         }  
  30.         if (needWake) {  
  31.             nativeWake(mPtr);  
  32.         }  
  33.         return true;  
  34.     }  
  35.   
  36.     ......  
  37. }  
        把訊息加入到訊息佇列時,分兩種情況,一種當前訊息佇列為空時,這時候應用程式的主執行緒一般就是處於空閒等待狀態了,這時候就要喚醒它,另一種情況是應用程式的訊息佇列不為空,這時候就不需要喚醒應用程式的主執行緒了,因為這時候它一定是在忙著處於訊息佇列中的訊息,因此不會處於空閒等待的狀態。

        第一種情況比較簡單,只要把訊息放在訊息佇列頭就可以了:

[java] view plaincopy
  1. msg.next = p;  
  2. mMessages = msg;  
  3. needWake = mBlocked; // new head, might need to wake up  
        第二種情況相對就比較複雜一些了,前面我們說過,當往訊息佇列中傳送訊息時,是可以指定訊息的處理時間的,而訊息佇列中的訊息,就是按照這個時間從小到大來排序的,因此,當把新的訊息加入到訊息佇列時,就要根據它的處理時間來找到合適的位置,然後再放進訊息佇列中去:

[java] view plaincopy
  1. Message prev = null;  
  2. while (p != null && p.when <= when) {  
  3.     prev = p;  
  4.     p = p.next;  
  5. }  
  6. msg.next = prev.next;  
  7. prev.next = msg;  
  8. needWake = false// still waiting on head, no need to wake up  
        把訊息加入到訊息佇列去後,如果應用程式的主執行緒正處於空閒等待狀態,就需要呼叫natvieWake函式來喚醒它了,這是一個JNI方法,定義在frameworks/base/core/jni/android_os_MessageQueue.cpp檔案中:

[java] view plaincopy
  1. static void android_os_MessageQueue_nativeWake(JNIEnv* env, jobject obj, jint ptr) {  
  2.     NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);  
  3.     return nativeMessageQueue->wake();  
  4. }  
        這個JNI層的NativeMessageQueue物件我們在前面分析訊息迴圈的時候建立好的,儲存在Java層的MessageQueue物件的mPtr成員變數中,這裡把它取回來之後,就呼叫它的wake函式來喚醒應用程式的主執行緒,這個函式也是定義在frameworks/base/core/jni/android_os_MessageQueue.cpp檔案中:

[java] view plaincopy
  1. void NativeMessageQueue::wake() {  
  2.     mLooper->wake();  
  3. }  
        這裡它又通過成員變數mLooper的wake函式來執行操作,這裡的mLooper成員變數是一個C++層實現的Looper物件,它定義在frameworks/base/libs/utils/Looper.cpp檔案中:

[java] view plaincopy
  1. void Looper::wake() {  
  2.     ......  
  3.   
  4.     ssize_t nWrite;  
  5.     do {  
  6.         nWrite = write(mWakeWritePipeFd, "W"1);  
  7.     } while (nWrite == -1 && errno == EINTR);  
  8.   
  9.     .......  
  10. }  
        這個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
  1. public class Looper {  
  2.     ......  
  3.   
  4.     public static final void loop() {  
  5.         Looper me = myLooper();  
  6.         MessageQueue queue = me.mQueue;  
  7.   
  8.         ......  
  9.   
  10.         while (true) {  
  11.             Message msg = queue.next(); // might block  
  12.             ......  
  13.   
  14.             if (msg != null) {  
  15.                 if (msg.target == null) {  
  16.                     // No target is a magic identifier for the quit message.  
  17.                     return;  
  18.                 }  
  19.   
  20.                 ......  
  21.   
  22.                 msg.target.dispatchMessage(msg);  
  23.                   
  24.                 ......  
  25.   
  26.                 msg.recycle();  
  27.             }  
  28.         }  
  29.     }  
  30.   
  31.     ......  
  32. }  
        它從訊息佇列中獲得訊息物件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
  1. public class Handler {  
  2.     ......  
  3.   
  4.     public void dispatchMessage(Message msg) {  
  5.         if (msg.callback != null) {  
  6.             handleCallback(msg);  
  7.         } else {  
  8.             if (mCallback != null) {  
  9.                 if (mCallback.handleMessage(msg)) {  
  10.                     return;  
  11.                 }  
  12.             }  
  13.             handleMessage(msg);  
  14.         }  
  15.     }  
  16.   
  17.     ......  
  18. }  
        這裡的訊息物件msg的callback成員變數和Handler類的mCallBack成員變數一般都為null,於是,就會呼叫Handler類的handleMessage函式來處理這個訊息,由於H類在繼承Handler類時,重寫了handleMessage函式,因此,這裡呼叫的實際上是H類的handleMessage函式,這個函式定義在frameworks/base/core/java/android/app/ActivityThread.java檔案中:

[java] view plaincopy
  1. public final class ActivityThread {    
  2.     
  3.     ......    
  4.     
  5.     private final class H extends Handler {    
  6.     
  7.         ......    
  8.     
  9.         public void handleMessage(Message msg) {    
  10.             ......    
  11.             switch (msg.what) {    
  12.             case LAUNCH_ACTIVITY: {    
  13.                 ActivityClientRecord r = (ActivityClientRecord)msg.obj;    
  14.     
  15.                 r.packageInfo = getPackageInfoNoCheck(    
  16.                     r.activityInfo.applicationInfo);    
  17.                 handleLaunchActivity(r, null);    
  18.             } break;    
  19.             ......    
  20.             }    
  21.     
  22.         ......    
  23.     
  24.     }    
  25.     
  26.     ......    
  27. }    
         因為前面在分析訊息的傳送時所舉的例子中,傳送的訊息的型別為H.LAUNCH_ACTIVITY,因此,這裡就會呼叫ActivityThread類的handleLaunchActivity函式來真正地處理這個訊息了,後面的具體過程就可以參考Android應用程式啟動過程原始碼分析這篇文章了。

         至此,我們就從訊息迴圈、訊息傳送和訊息處理三個部分分析完Android應用程式的訊息處理機制了,為了更深理解,這裡我們對其中的一些要點作一個總結:

         A. Android應用程式的訊息處理機制由訊息迴圈、訊息傳送和訊息處理三個部分組成的。

         B. Android應用程式的主執行緒在進入訊息迴圈過程前,會在內部建立一個Linux管道(Pipe),這個管道的作用是使得Android應用程式主執行緒在訊息佇列為空時可以進入空閒等待狀態,並且使得當應用程式的訊息佇列有訊息需要處理時喚醒應用程式的主執行緒。

         C. Android應用程式的主執行緒進入空閒等待狀態的方式實際上就是在管道的讀端等待管道中有新的內容可讀,具體來說就是是通過Linux系統的Epoll機制中的epoll_wait函式進行的。

         D. 當往Android應用程式的訊息佇列中加入新的訊息時,會同時往管道中的寫端寫入內容,通過這種方式就可以喚醒正在等待訊息到來的應用程式主執行緒。

         E. 當應用程式主執行緒在進入空閒等待前,會認為當前執行緒處理空閒狀態,於是就會呼叫那些已經註冊了的IdleHandler介面,使得應用程式有機會在空閒的時候處理一些事情。

相關文章