前言
前文詳細分析了Java層的消息循環機制的工作原理,在分析MessageQueue的過程中,我們遇到了nativePollOnce()
和nativeWake()
方法的調用,下面我們就深入到Native層的消息機制來看看它背后的運作原理。
Native層的消息機制
一、NativeMessageQueue的相關邏輯
1、NativeMessageQueue的構建
首先,我們來看看Java層的MessageQueue的構造函數:
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
這里調用了一個native方法,實際上這里初始化了native層的消息隊列,并返回了該消息隊列的頭部指針地址。由于這個是native方法,這里利用了JNI機制調用本地代碼,JNI的相關知識筆者在前面的文章也有說到,這里就不再贅述。我們直接來看本地代碼:(frameworks/base/core/jni/android_os_MessageQueue.cpp):
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); //創建了一個本地的消息隊列
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env); //增加強引用指針計數,這里與RefBase,類似于智能指針的概念
return reinterpret_cast<jlong>(nativeMessageQueue); //強制類型轉換,返回jlong類型,實際上是指針的值
}
這里引入了NativeMessageQueue
,顧名思義,這應該是native層的消息隊列,我們用UML類圖來看看它的類結構:
其中,RefBase是基類,它的作用比較特殊,在Android的native代碼中,大部分的類都是繼承自RefBase,作用有點類似于Java中的Object。實際上,它的作用與實現智能指針有關,便于native層的垃圾回收的實現(因為C++沒有GC機制,我們需要手動實現對象的創建和回收操作)。這里不對RefBase做過多的深究,我們把關注點放回
NativeMessageQueue
。
NativeMessageQueue
是native層的消息隊列,雖然它稱作消息隊列,但實際上它是一個空殼,它內部并沒有維護消息的隊列或者鏈表,它把涉及消息的相關操作都交給了Looper
去處理。與Java層一樣的是,一條線程只會有一個Looper
,這是因為native層也有著一套線程獨立變量的機制,當前線程的變量只與當前線程有關。我們來看看NativeMessageQueue
的構造方法:
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread(); //獲取當前線程的Looper對象
if (mLooper == NULL) { //如果沒有,那么初始化Looper對象
mLooper = new Looper(false);
Looper::setForThread(mLooper); //Looper與當前線程綁定
}
}
通過NativeMessageQueue
的構造方法可以看出,這里實例化了一個Looper
,而這個Looper
則是與線程相關的,因此我們可以推測Looper
實際上承擔了消息隊列的實際功能,NativeMessageQueue
對外表現為消息隊列,也有相關的方法,但實際上它把核心邏輯都移交給了Looper
去處理。
小結:Java層的MessageQueue
被創建的時候,同時會創建一個Native層的NativeMessageQueue
,并初始化一個native的Looper
。經過JNI的調用返回,NativeMessageQueue
對象的指針地址會返回給Java層的MessageQueue
,保存在mPtr
這個成員變量內。
2、NativeMessageQueue#pollOnce
經過上面的NativeMessageQueue的初始化后,我們就能正常使用它了。還記得我們在Java層的MessageQueue#next()
方法內曾經看到過這個調用嗎?nativePollOnce(ptr, nextPollTimeoutMillis)
,這里調用了native的方法,我們來看看它的nativec層的源碼:
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
方法很簡單,這里傳進來的ptr參數實際上就是MessageQueue.mPtr
變量,經過強制類型轉換后,把ptr轉換成了NativeMessageQueue指針,然后調用這個對象的pollOnce(args)
方法。這個過程可以理解為:在某一線程內,Java層持有Native層的NativeMessageQueue的指針地址值,經過JNI調用把該地址傳遞過來,native層根據這個地址找到了這個對象,強制類型轉換成了NativeMessageQueue
對象。簡單地說,Java層的MessageQueue對應了Native層的NativeMessageQueue。
下面,我們來看nativeMessageQueue->pollOnce(env, obj, timeoutMillis)
:
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
mLooper->pollOnce(timeoutMillis); //把邏輯交給Looper去處理
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj); //env是JNI環境,這里的異常會拋給Java層
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
邏輯很簡單,這里調用了mLooper->pollOnce(timeoutMillis)
方法,并把超時時間傳遞了進去,結合函數名字,我們可以合理推測:該函數內部在超時時間內進行native的消息處理,達到了超時時間就會JNI調用返回,以便處理Java層的消息。我們先記住這個推測,待會分析Looper的時候再來看這個推測是否正確。
3、NativeMessageQueue#wake
同樣地,我們之前在討論MessageQueue#enqueueMessage()
的時候,會發現進行了nativeWake()
調用,那么我們直接看源碼:
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
void NativeMessageQueue::wake() {
mLooper->wake();
}
顯然,根據ptr
找到對應的NativeMessageQueue
,然后調用了mLooper->wake()
,把邏輯交給了Looper去處理。那么到目前為止,一切問題的關鍵都指向了Looper
,我們開始探究native的Looper吧。
二、native Looper的工作原理
Looper是整個Native層消息機制的核心所在,大部分功能都是Looper完成的。要準確理解native Looper,我們首先就要對它的類結構有個整體的認識,下面筆者給出Looper的UML類圖方便讀者的理解。
1、native Looper的整體認識
Looper的類結構被定義在/system/core/libutils/include/utils/Looper.h
,源碼地址為Looper.h。結合類結構,我們可以清晰地畫出如下的UML類圖:
結合
Looper.h
的源碼和上面的UML類圖,下面先列舉幾個與Looper有關的類或成員變量,以便后面的源碼閱讀。①mWakeEventFd:用于事件通知的文件描述符,在這里表示喚醒Looper的文件描述符。實際上它指向一個
eventfd
對象,該對象可以實現事件的等待和通知機制。②mEpollFd:
epoll
的句柄,指向一個epoll
對象,有關epoll
的使用都要經過該句柄。epoll
簡單來說,是Linux下的一種I/O事件通知機制。epoll
監聽了eventfd
,當該文件描述符的緩沖區為空時,epoll
發出可寫信號,當文件描述符的緩沖區不空時,發出可讀信號。利用這樣的機制,能實現線程間的通信。③MessageEnvelop:內部封裝了
Message
、MessageHandler
和uptime
。其中Message
是native層發送的消息,而MessageHandler
內部有一個回調函數,當消息接收方接收并處理消息后就會回調該函數。uptime
是消息的觸發時間。④Request:封裝了
fd
、events
、callback
等參數,實際上這是Looper監聽的一種特殊消息,可以稱之為事件,它是以文件描述符形式存在的。通過addFd(args)
把fd添加到epoll
的監聽隊列中,然后封裝成Request
對象,加入到Looper.mRequest
內,然后等待事件的發生。⑤Response:當
epoll
偵測到某一fd的緩沖池發生了改變后,Looper會找到該fd對應的Request,把它進一步封裝成Response,然后加入mResponse
等待Looper處理該消息。
2、Looper的創建與初始化
上面詳細講述了與Looper有關的一些知識,在此基礎上我們繼續探索Looper的原理,首先,我們從Looper的構造方法看起。(源碼位置在:/system/core/libutils/Looper.cpp)
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); //創建一個用于喚醒Looper的文件描述符
LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd: %s",
strerror(errno));
AutoMutex _l(mLock);
rebuildEpollLocked(); //重建一個epoll
}
void Looper::rebuildEpollLocked() {
// 如果已經有一個epoll了,那么關閉它
if (mEpollFd >= 0) {
#if DEBUG_CALLBACKS
ALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this);
#endif
close(mEpollFd);
}
// 通過Linux系統調用 創建一個epoll實例
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
struct epoll_event eventItem; //epoll_event 封裝了fd、event等
memset(& eventItem, 0, sizeof(epoll_event)); // 初始化為0
eventItem.events = EPOLLIN; //event:EPOLLIN 表示讀事件,即對應的連接可讀(緩沖區有值)
eventItem.data.fd = mWakeEventFd;
// epoll_ctl 系統調用,這里epoll添加了對mWakeEventFd的監聽,
// 當epoll監聽到mWakeEventFd的eventItem出現時,就會通知Looper
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",
strerror(errno));
// 對mRequest內的所有fd重新添加epoll監聽
for (size_t i = 0; i < mRequests.size(); i++) {
const Request& request = mRequests.valueAt(i);
struct epoll_event eventItem;
request.initEventItem(&eventItem);
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",
request.fd, strerror(errno));
}
}
}
Looper的創建和初始化過程的代碼邏輯很清晰,主要是做了以下幾件事情:
(1)調用eventfd(args)
方法創建一個用于喚醒Looper的文件描述符mWakeEventFd
。
(2)進行epoll_create(int)
系統調用,創建一個epoll
,用于輪詢IO以及通知Looper有關的fd是否發生了改變,并把epoll
句柄保存在mEpollFd
內。
(3)進行epoll_ctl(args)
系統調用,把mWakeEventFd
添加到epoll的監聽列表中。
(4)遍歷mRequests
,把所有通過addFd(args)
添加進來的文件描述符fd再次加到這個新的epoll的監聽列表內。(舊的epoll已經被close了)
3、發送消息/添加事件監聽
我們知道,native層的Looper主要處理兩種消息:一個是Message
,另一個是eventItem
(epoll監聽文件描述符fd的變化)。下面我們來了解以下消息的發送過程或事件的添加過程。
(1)Looper#sendMessage
void Looper::sendMessage(const sp<MessageHandler>& handler, const Message& message) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
sendMessageAtTime(now, handler, message);
}
void Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,
const Message& message) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
sendMessageAtTime(now + uptimeDelay, handler, message);
}
void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
const Message& message) {
#if DEBUG_CALLBACKS
ALOGD("%p ~ sendMessageAtTime - uptime=%" PRId64 ", handler=%p, what=%d",
this, uptime, handler.get(), message.what);
#endif
size_t i = 0;
{ // acquire lock
AutoMutex _l(mLock);
size_t messageCount = mMessageEnvelopes.size();
// 在mMessageEnvelopes內尋找消息要插入的位置,條件是msg的uptime要比后者的小,但比前者大
// 這說明在這個消息列表內,下標越小,消息的觸發時間越早
while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) {
i += 1;
}
MessageEnvelope messageEnvelope(uptime, handler, message);
mMessageEnvelopes.insertAt(messageEnvelope, i, 1);
// 如果Looper正在發送消息,那么直接返回。因為Looper在發送完消息后,
// 會計算下一條消息的觸發時間進而處理該消息
if (mSendingMessage) {
return;
}
} // release lock
// 如果消息被插入在消息列表頭部,立刻喚醒looper
if (i == 0) {
wake();
}
}
發送一個消息時,不但要有Message
實例,同時也要有MessageHandler
,這是消息被處理的時候進行回調的方法。類似于Java層的Messaeg.callback
。緊接著,在把消息添加到消息列表的時候,會進行加鎖,防止出現并發錯誤。如果當前Looper沒有在處理消息并且插入的消息放在了列表頭部,則需要去喚醒Looper。
(2)Looper#addFd
上面是發送一個消息然后等待Looper的處理,而這個方法則是添加一個文件描述符fd,然后讓epoll去監聽它的變化,如果產生了變化則會得到Looper的處理。
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
#if DEBUG_CALLBACKS
ALOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident,
events, callback.get(), data);
#endif
//省略...
{ // acquire lock
AutoMutex _l(mLock);
// 封裝成Request對象
Request request;
request.fd = fd;
request.ident = ident;
request.events = events;
request.seq = mNextRequestSeq++;
request.callback = callback;
request.data = data;
if (mNextRequestSeq == -1) mNextRequestSeq = 0; // reserve sequence number -1
struct epoll_event eventItem;
request.initEventItem(&eventItem);
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex < 0) {
// 如果mRequest內沒有該Request,那么添加到epoll監聽列表內
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d: %s", fd, strerror(errno));
return -1;
}
mRequests.add(fd, request);
} else {
// 如果mRequest已經有該Request,那么修改epoll監聽列表對應的fd的內容
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
if (epollResult < 0) {
// 異常狀態處理
// 省略...
}
mRequests.replaceValueAt(requestIndex, request); //更新mRequest
}
} // release lock
return 1;
}
首先將fd、LooperCallback、*data等封裝成了Request對象,然后經過epoll_ctl
系統調用,添加到epoll
監聽列表內,此時epoll
就會對fd進行輪詢,如果發生了變化就能得到通知。所以可以通過文件描述符的形式添加到Looper,然后我們在別的線程改變它,那么Looper所在的線程就能就能知道它的改變,然后調用回調方法,這樣就實現了線程的切換。
4、Looper的喚醒操作
Looper的喚醒操作指的是,往mWakeEventFd
的緩沖區寫入一個數字,然后Looper的pollOnce(args)
就會獲取到該事件的發生,然后可以開始處理消息,否則該方法將會阻塞直到超時。
我們回憶下上一篇文章,在調用Message#enqueueMessage
插入一條消息的時候,如果Java的消息隊列處于阻塞狀態、隊列頭部是消息屏障以及馬上有一個異步消息要處理,那么就會進行nativeWake()
的本地調用,然后進一步調用Looper#wake()
以喚醒Looper。當native的消息處理完成后,會導致Java層的nativePollOnce()
的調用返回,從而讓Java層處理消息。因此,Looper#wake
和Looper#pollOnce
起著重要的橋梁作用,它讓Java層和native層的消息機制得以聯系和連續運作。
下面,我們來看Looper#wake
:
void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ wake", this);
#endif
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s",
mWakeEventFd, strerror(errno));
}
}
}
首先定義了一個64位的整型變量為1,然后通過write()
方法,往mWakeEventFd
寫入1。此時epoll
會偵測到這個寫入事件,然后就會通知Looper了。
5、Looper處理消息的過程
如果Looper想要運轉起來,還需要調用一個方法,那就是pollOnce
。該方法驅動了native Looper的消息循環。我們先來看源碼:
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
// 先處理被添加到mResponses列表的事件,這些事件都沒有callback,
// 因此只能返回給調用者根據ident去處理
while (mResponseIndex < mResponses.size()) {
const Response& response = mResponses.itemAt(mResponseIndex++);
int ident = response.request.ident;
if (ident >= 0) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
if (outFd != NULL) *outFd = fd;
if (outEvents != NULL) *outEvents = events;
if (outData != NULL) *outData = data;
return ident;
}
}
if (result != 0) {
if (outFd != NULL) *outFd = 0;
if (outEvents != NULL) *outEvents = 0;
if (outData != NULL) *outData = NULL;
return result;
}
result = pollInner(timeoutMillis); //進一步處理,并傳入超時時間
}
}
int Looper::pollInner(int timeoutMillis) {
// 調整超時時間,根據當前超時時間和mNextMessageUptime來判斷
// 如果傳遞進來的超時時間大于mNextMessageUptime,就要把超時時間改成這個觸發時間
// 否則,下一條Message將不能在準確時間內得到處理
if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
if (messageTimeoutMillis >= 0
&& (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
timeoutMillis = messageTimeoutMillis;
}
}
// Poll.
int result = POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;
// 表示正在輪詢
mPolling = true;
// 創建epoll_event數組,以保存epoll結果
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
// 進行系統調用,阻塞式等待,收集在epoll監控的事件中已經發生的事件,超過timeoutMillis則返回
// 實際上這里epoll監聽所有已添加的fd的改變,比如寫入了“1”,那么就能收集到該事件
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// 取消輪詢狀態
mPolling = false;
// Acquire lock.
mLock.lock();
// Rebuild epoll set if needed.
if (mEpollRebuildRequired) {
mEpollRebuildRequired = false;
rebuildEpollLocked();
goto Done;
}
// Check for poll error.
if (eventCount < 0) {
if (errno == EINTR) {
goto Done;
}
ALOGW("Poll failed with an unexpected error: %s", strerror(errno));
result = POLL_ERROR;
goto Done;
}
// Check for poll timeout.
if (eventCount == 0) {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - timeout", this);
#endif
result = POLL_TIMEOUT;
goto Done;
}
// 處理所有在epoll收集到的事件
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeEventFd) {
if (epollEvents & EPOLLIN) {
awoken(); //從mWakeEventFd中讀取“1”,以便下一次的wake()操作,即清空緩沖區
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
} else {
// 根據已觸發的fd事件,找到相應的Request,然后添加到mResponses內
// 表示該事件已經被監聽到,等待處理
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex >= 0) {
int events = 0;
if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
pushResponse(events, mRequests.valueAt(requestIndex));
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
"no longer registered.", epollEvents, fd);
}
}
}
Done: ;
// 處理所有的Message 這里只處理觸發時間小于當前時間的,
// 如果觸發時間還沒到的消息,等待下一次pollOnce()再處理
mNextMessageUptime = LLONG_MAX;
while (mMessageEnvelopes.size() != 0) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
if (messageEnvelope.uptime <= now) {
{ // obtain handler
sp<MessageHandler> handler = messageEnvelope.handler;
Message message = messageEnvelope.message;
mMessageEnvelopes.removeAt(0);
mSendingMessage = true;
mLock.unlock();
handler->handleMessage(message); //利用MessageHandler來處理消息,即回調函數
} // release handler
mLock.lock();
mSendingMessage = false;
result = POLL_CALLBACK;
} else {
// The last message left at the head of the queue determines the next wakeup time.
mNextMessageUptime = messageEnvelope.uptime;
break;
}
}
// Release lock.
mLock.unlock();
// 處理所有的Response,即已觸發的fd事件
for (size_t i = 0; i < mResponses.size(); i++) {
Response& response = mResponses.editItemAt(i);
if (response.request.ident == POLL_CALLBACK) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
// 回調callback
int callbackResult = response.request.callback->handleEvent(fd, events, data);
if (callbackResult == 0) {
removeFd(fd, response.request.seq);
}
response.request.callback.clear();
result = POLL_CALLBACK;
}
}
return result;
}
一眼看去,整個pollOnce
和pollInner
的代碼非常長,但實際上它的邏輯非常地清晰明了,詳細的分析已經寫在了注釋里面,這里簡單地總結以下它所作的工作:
(1)在pollOnce
方法中,首先會對所有Response中沒有callback的事件進行處理,這會返回ident給調用者,讓調用者自己去處理這個消息。
(2)在pollInner
方法內,首先超時時間的調整操作,這要兼顧到一個常規Message的觸發,不能讓epoll
阻塞過久。
(3)緊接著,進行了epoll_wait
系統調用,阻塞式等待epoll
返回事件,epoll
中監聽到的事件會保存在eventItems[EPOLL_MAX_EVENTS]
內。如果超時時間到了,就返回。
(4)對所有監聽到的事件進行處理,根據事件找到相應的Request,然后封裝成Response,等待Looper的處理。
(5)處理所有到達觸發事件的常規Message,進行handler的回調。
(6)處理所有Response,進行callback回調。
總結
上面完整地分析了Looper的工作原理和運行機制,現在整理一下所有相關的知識點。
①native Looper在初始化的時候,會創建epoll
用以監聽文件描述符fd的改變,同時實例化了一個mWakeEventFd
,這是用來喚醒Looper的fd,然后把它添加到epoll
監聽隊列內。
②Java層的Looper在loop()的過程中,會調用MessageQueue#next()
方法,進而調用nativePollOnce()
方法,而該方法又交給Looper#pollOnce
處理。這表示Java層的消息在處理之前,會先處理native層的消息,直到JNI調用返回。
③在native Looper的循環過程中,會使用epoll_wait
系統調用,阻塞式等待相關的fd事件的發生。然后再處理Message和fd event。
④Looper在阻塞的時候,可以通過wake()
方法來喚醒它,通過往mWakeEventFd
寫入"1",會導致epoll_wait
返回,從而取消輪詢狀態。