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文件中:
view plain
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文件中:
view plain
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();
}
……
}
函數prepareMainLooper做的事情其實就是在線程中創建一個Looper對象,這個Looper對象是存放在sThreadLocal成員變量裡面的,成員變量sThreadLocal的類型為ThreadLocal,表示這是一個線程局部變量,即保證每一個調用瞭prepareMainLooper函數的線程裡面都有一個獨立的Looper對象。在線程是創建Looper對象的工作是由prepare函數來完成的,而在創建Looper對象的時候,會同時創建一個消息隊列MessageQueue,保存在Looper的成員變量mQueue中,後續消息就是存放在這個隊列中去。消息隊列在Android應用程序消息處理機制中最重要的組件,因此,我們看看它的創建過程,即它的構造函數的實現,實現frameworks/base/core/java/android/os/MessageQueue.java文件中:
view plain
public class MessageQueue {
……
private int mPtr; // used by native code
private native void nativeInit();
MessageQueue() {
nativeInit();
}
……
}
它的初始化工作都交給JNI方法nativeInit來實現瞭,這個JNI方法定義在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:
view plain
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文件中,它的創建過程如下所示:
view plain
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成員變量裡面:
view plain
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文件中:
view plain
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
……
}
這個構造函數做的事情非常重要,它跟我們後面要介紹的應用程序主線程在消息隊列中沒有消息時要進入等待狀態以及當消息隊列有消息時要把應用程序主線程喚醒的這兩個知識點息息相關。它主要就是通過pipe系統調用來創建瞭一個管道瞭:
view plain
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專用的文件描述符:
view plain
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
傳入的參數EPOLL_SIZE_HINT是在這個mEpollFd上能監控的最大文件描述符數。
接著還要通過epoll_ctl函數來告訴epoll要監控相應的文件描述符的什麼事件:
view plain
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,它要監控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函數進入到消息循環中去瞭:
view plain
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();
}
}
}
……
}
這裡就是進入到消息循環中去瞭,它不斷地從消息隊列mQueue中去獲取下一個要處理的消息msg,如果消息的target成員變量為null,就表示要退出消息循環瞭,否則的話就要調用這個target對象的dispatchMessage成員函數來處理這個消息,這個target對象的類型為Handler,下面我們分析消息的發送時會看到這個消息對象msg是如設置的。
這個函數最關鍵的地方便是從消息隊列中獲取下一個要處理的消息瞭,即MessageQueue.next函數,它實現frameworks/base/core/java/android/os/MessageQueue.java文件中:
view plain
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;
}
}
……
}
調用這個函數的時候,有可能會讓線程進入等待狀態。什麼情況下,線程會進入等待狀態呢?兩種情況,一是當消息隊列中沒有消息時,它會使線程進入等待狀態;二是消息隊列中有消息,但是消息指定瞭執行的時間,而現在還沒有到這個時間,線程也會進入等待狀態。消息隊列中的消息是按時間先後來排序的,後面我們在分析消息的發送時會看到。
執行下面語句是看看當前消息隊列中有沒有消息:
view plain
nativePollOnce(mPtr, nextPollTimeoutMillis);
這是一個JNI方法,我們等一下再分析,這裡傳入的參數mPtr就是指向前面我們在JNI層創建的NativeMessageQueue對象瞭,而參數nextPollTimeoutMillis則表示如果當前消息隊列中沒有消息,它要等待的時候,for循環開始時,傳入的值為0,表示不等待。
當前nativePollOnce返回後,就去看看消息隊列中有沒有消息:
view plain
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消息處理,否則的話就要等待到消息的執行時間:
view plain
nextPollTimeoutMillis = (int) Math.min(when – now, Integer.MAX_VALUE);
如果消息隊列中沒有消息,那就要進入無窮等待狀態直到有新消息瞭:
view plain
nextPollTimeoutMillis = -1;
-1表示下次調用nativePollOnce時,如果消息中沒有消息,就進入無限等待狀態中去。
這裡計算出來的等待時間都是在下次調用nativePollOnce時使用的。
這裡說的等待,是空閑等待,而不是忙等待,因此,在進入空閑等待狀態前,如果應用程序註冊瞭IdleHandler接口來處理一些事情,那麼就會先執行這裡IdleHandler,然後再進入等待狀態。IdlerHandler是定義在MessageQueue的一個內部類:
view plain
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();
}
……
}
它隻有一個成員函數queueIdle,執行這個函數時,如果返回值為false,那麼就會從應用程序中移除這個IdleHandler,否則的話就會在應用程序中繼續維護著這個IdleHandler,下次空閑時仍會再執會這個IdleHandler。MessageQueue提供瞭addIdleHandler和removeIdleHandler兩註冊和刪除IdleHandler。
回到MessageQueue函數中,它接下來就是在進入等待狀態前,看看有沒有IdleHandler是需要執行的:
view plain
// 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,那下面的邏輯就不執行瞭,通過continue語句直接進入下一次循環,否則就要把註冊在mIdleHandlers中的IdleHandler取出來,放在mPendingIdleHandlers數組中去。
接下來就是執行這些註冊瞭的IdleHanlder瞭:
view plain
// 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);
}
}
}
執行完這些IdleHandler之後,線程下次調用nativePollOnce函數時,就不設置超時時間瞭,因為,很有可能在執行IdleHandler的時候,已經有新的消息加入到消息隊列中去瞭,因此,要重置nextPollTimeoutMillis的值:
view plain
// 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函數之後,我們就要深入分析一下JNI方法nativePollOnce瞭,看看它是如何進入等待狀態的,這個函數定義在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:
view plain
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函數:
view plain
void NativeMessageQueue::pollOnce(int timeoutMillis) {
mLooper->pollOnce(timeoutMillis);
}
這裡將操作轉發給mLooper對象的pollOnce函數處理,這裡的mLooper對象是在C++層的對象,它也是在前面在JNI層創建的NatvieMessageQueue對象時創建的,它的pollOnce函數定義在frameworks/base/libs/utils/Looper.cpp文件中:
view plain
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的定義如下:
view plain
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:
view plain
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
回憶一下前面的Looper的構造函數,我們在裡面設置瞭要監控mWakeReadPipeFd文件描述符的EPOLLIN事件。
當mEpollFd所監控的文件描述符發生瞭要監控的IO事件後或者監控時間超時後,線程就從epoll_wait返回瞭,否則線程就會在epoll_wait函數中進入睡眠狀態瞭。返回後如果eventCount等於0,就說明是超時瞭:
view plain
if (eventCount == 0) {
……
result = ALOOPER_POLL_TIMEOUT;
goto Done;
}
如果eventCount不等於0,就說明發生要監控的事件:
view plain
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的實現很簡單,它隻是把管道中的內容都讀取出來:
view plain
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文件中:
view plain
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);
}
……
}
……
}
這裡把相關的參數都封裝成一個ActivityClientRecord對象r,然後調用queueOrSendMessage函數來往應用程序的消息隊列中加入一個新的消息(H.LAUNCH_ACTIVITY),這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
view plain
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函數中,又進一步把上面傳進來的參數封裝成一個Message對象msg,然後通過mH.sendMessage函數把這個消息對象msg加入到應用程序的消息隊列中去。這裡的mH是ActivityThread類的成員變量,它的類型為H,繼承於Handler類,它定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
view plain
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實例:
view plain
public final class ActivityThread {
……
public static final void main(String[] args) {
……
ActivityThread thread = new ActivityThread();
thread.attach(false);
……
}
}
在創建這個實例的時候,就會同時創建其成員變量mH瞭:
view plain
public final class ActivityThread {
……
final H mH = new H();
……
}
前面說過,H類繼承於Handler類,因此,當創建這個H對象時,會調用Handler類的構造函數,這個函數定義在frameworks/base/core/java/android/os/Handler.java文件中:
view plain
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文件中:
view plain
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文件中:
view plain
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,即這個消息最終是由誰來處理的:
view plain
msg.target = this;
這裡將它賦值為this,即表示這個消息最終由這個Handler對象來處理,即由ActivityThread對象的mH成員變量來處理。
函數最後調用queue.enqueueMessage來把這個消息加入到應用程序的消息隊列中去,這個函數實現在frameworks/base/core/java/android/os/MessageQueue.java文件中:
view plain
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;
}
……
}
把消息加入到消息隊列時,分兩種情況,一種當前消息隊列為空時,這時候應用程序的主線程一般就是處於空閑等待狀態瞭,這時候就要喚醒它,另一種情況是應用程序的消息隊列不為空,這時候就不需要喚醒應用程序的主線程瞭,因為這時候它一定是在忙著處於消息隊列中的消息,因此不會處於空閑等待的狀態。
第一種情況比較簡單,隻要把消息放在消息隊列頭就可以瞭:
view plain
msg.next = p;
mMessages = msg;
needWake = mBlocked; // new head, might need to wake up
第二種情況相對就比較復雜一些瞭,前面我們說過,當往消息隊列中發送消息時,是可以指定消息的處理時間的,而消息隊列中的消息,就是按照這個時間從小到大來排序的,因此,當把新的消息加入到消息隊列時,就要根據它的處理時間來找到合適的位置,然後再放進消息隊列中去:
view plain
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
把消息加入到消息隊列去後,如果應用程序的主線程正處於空閑等待狀態,就需要調用natvieWake函數來喚醒它瞭,這是一個JNI方法,定義在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:
view plain
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文件中:
view plain
void NativeMessageQueue::wake() {
mLooper->wake();
}
這裡它又通過成員變量mLooper的wake函數來執行操作,這裡的mLooper成員變量是一個C++層實現的Looper對象,它定義在frameworks/base/libs/utils/Looper.cpp文件中:
view plain
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文件中:
view plain
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後,就會調用它的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文件中:
view plain
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文件中:
view plain
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接口,使得應用程序有機會在空閑的時候處理一些事情。
作者“老羅的Android之旅”