Android Handler機制系列文章整體內容如下:
- Android Handler機制1之Thread
- Android Handler機制2之ThreadLocal
- Android Handler機制3之SystemClock類
- Android Handler機制4之Looper與Handler簡介
- Android Handler機制5之Message簡介與消息對象對象池
- Android Handler機制6之MessageQueue簡介
- Android Handler機制7之消息發送
- Android Handler機制8之消息的取出與消息的其他操作
- Android Handler機制9之Handler的Native實現前奏之Linux IO多路復用
- Android Handler機制10之Handdler的Native實現Native的實現
- Android Handler機制11之Handler機制總結
- Android Handler機制12之Callable、Future和FutureTask
- Android Handler機制13之AsyncTask源碼解析
一、簡述
前面的文章講解了Java層的消息處理機制,其中MessageQueue類里面涉及到的多個Native方法,除了MessageQueue的native方法,native本身也有一套完整的消息機制,處理native消息。在整個消息機制中,MessageQueue是連接Java層和Native層的紐帶,換而言之,Java層可以向MessageQueue消息隊列中添加消息,Native層也可以向MessageQueue消息隊列中添加消息。
Native的層關系圖:
二、MessageQueue
在MessageQueue的native方法如下:
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
在Android Handler機制6之MessageQueue簡介中的五、native層代碼的初始化中 說了MessaegQueue構造函數調用了nativeInit(),為了更好的理解,我們便從MessageQueue構造函數開始說起
(一)、 nativeInit() 函數
nativeInit() 的主要作用是初始化,是在MessageQueue的構造函數中調用
代碼在MessageQueue.java 68行
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
// 通過JNI調用了Native層的相關函數,導致了NativeMessageQueue的創建
mPtr = nativeInit();
}
MessageQueue只是有一個構造函數,該構造函數是包內可見的,其內部就兩行代碼,分別是設置了MessageQueue是否可以退出和native層代碼的相關初始化
在MessageQueue的構造函數里面調用 nativeInit(),我們來看下
代碼在MessageQueue.java 61行
private native static long nativeInit();
根據Android跨進程通信IPC之3——關于"JNI"的那些事中知道,nativeInit這個native方法對應的是android_os_MessageQueue.cpp里面的android_os_MessageQueue_nativeInit(JNIEnv* , jclass )函數
1、jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz)方法
代碼在android_os_MessageQueue.cpp 172 行
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
// 在Native層又創建了NativeMessageQueue
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
// 這里返回值是Java層的mPtr,因此mPtr實際上是Java層MessageQueue與NativeMessesageQueue的橋梁
return reinterpret_cast<jlong>(nativeMessageQueue);
}
此時Java層和Native層的MessageQueue被mPtr連接起來了,NativeMessageQueue只是Java層MessageQueue在Native層的體現,其本身并沒有實現Queue的數據結構,而是從其父類MessageQueue中繼承mLooper變量。與Java層類型,這個Looper也是線程級別的單列。
代碼中是直接new 的NativeMessageQueue無參構造函數,那我們那就來看下
2、NativeMessageQueue無參構造函數
NativeMessageQueue是android_os_MessageQueue.cpp的內部類
代碼在android_os_MessageQueue.cpp 78行
NativeMessageQueue::NativeMessageQueue() : mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
// 獲取TLS中的Looper對象
mLooper = Looper::getForThread();
if (mLooper == NULL) {
// 創建native層的Looper對象
mLooper = new Looper(false);
// 保存native 層的Looper到TLS中(線程級單例)
Looper::setForThread(mLooper);
}
}
- Looper::getForThread():功能類比于Java層的Looper.myLooper();
- Looper::setForThread(mLooper):功能類比于Java層的ThreadLocal.set()
通過上述代碼我們知道:
- 1、Java層的Looper的創建導致了MessageQueue的創建,而在Native層則剛剛相反,NativeMessageQueue的創建導致了Looper的創建
- 2、MessageQueue是在Java層與Native層有著緊密的聯系,但是此次Native層的Looper與Java層的Looper沒有任何關系。
- 3、Native層的Looper創建和Java層的也完全不一樣,它利用了Linux的epoll機制檢測了Input的fd和喚醒fd。從功能上來講,這個喚醒fd才是真正處理Java Message和Native Message的鑰匙。
PS:5.0以上的版本Loooer定義在System/core下
上面說了半天,那我們就來看下Native的Looper的構造函數
3、 Native層的Looper的構造函數
代碼在Looper.cpp 71行
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
/** 這才是Linux后來才有的東西,負責線程通信,替換了老版本的pipe */
//構造喚醒時間的fd
mWakeEventFd = eventfd(0, EFD_NONBLOCK);
AutoMutex _l(mLock);
rebuildEpollLocked();
}
這個方法重點就是調用了rebuildEpollLocked(); 函數
PS:這里說一下eventfd,event具體與pipe有點像,用來完成兩個線程之間(現在也支持進程級別),能夠用來作為線程之間通訊,類似于pthread_cond_t。
4、 Looper::rebuildEpollLocked() 函數
代碼在Looper.cpp 140行
void Looper::rebuildEpollLocked() {
// Close old epoll instance if we have one.
if (mEpollFd >= 0) {
#if DEBUG_CALLBACKS
ALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this);
#endif
// 關閉老的epoll實例
close(mEpollFd);
}
// Allocate the new epoll instance and register the wake pipe.
// 創建一個epoll實例,并注冊wake管道。
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);
struct epoll_event eventItem;
// 清空,把未使用的數據區域進行置0操作
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
//關注EPOLLIN事件,也就是可讀
eventItem.events = EPOLLIN;
//設置Fd
eventItem.data.fd = mWakeEventFd;
//將喚醒事件(mWakeEventFd)添加到epoll實例(mEpollFd),其實只是為epoll放置一個喚醒機制
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. errno=%d",
errno);
// 這里主要添加的是Input事件,如鍵盤、傳感器輸入,這里基本上是由系統負責。
for (size_t i = 0; i < mRequests.size(); i++) {
const Request& request = mRequests.valueAt(i);
struct epoll_event eventItem;
request.initEventItem(&eventItem);
// 將request的隊列事件,分別添加到epoll實例
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, errno=%d",
request.fd, errno);
}
}
這里一定要明白的是,添加這些fd除了mWakeEventFd負責解除阻塞讓程序繼續運行,從而處理Native Message和Java Message外,其他fd與Message的處理其實毫無關系。此時Java層與Native聯系如下:
這時候大家可能有點蒙,所以我下面補充1個知識點,希望能幫助大家
8、 小結
所有整個流程整理如下圖:
(二) nativeDestroy()
nativeDestroy是在MessageQueue的dispose()方法中調用,主要用于清空回收
代碼在MessageQueue.java 84行
// Disposes of the underlying message queue.
// Must only be called on the looper thread or the finalizer.
private void dispose() {
if (mPtr != 0) {
// native方法
nativeDestroy(mPtr);
mPtr = 0;
}
}
根據Android跨進程通信IPC之3——關于"JNI"的那些事中知道,nativeDestroy()這個native方法對應的是android_os_MessageQueue.cpp里面的android_os_MessageQueue_nativeDestroy()函數
1、android_os_MessageQueue_nativeDestroy()函數
代碼在MessageQueue.java 183行
static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
// 強制類型轉換為nativeMessageQueue
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
//調用nativeMessageQueue的decStrong()函數
nativeMessageQueue->decStrong(env);
}
我們看到上面代碼是
- 首先,將Java層傳遞下來的mPtr轉換為nativeMessageQueue
- 其次,nativeMessageQueue調用decStrong(env)
nativeMessageQueue繼承自RefBase類,所以decStrong最終調用的是RefBase.decStrong()。
Android跨進程通信IPC之4——AndroidIPC基礎2的第五部分五、智能指針,中對智能指針有詳細描述,這里就不過多介紹了
2、總體流程圖
(三) nativePollOnce()
nativePollOnce()是在MessageQueue的next()方法中調用,用于提取消息的調用鏈
代碼在MessageQueue.java 323行
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
for (;;) {
...
//阻塞操作
nativePollOnce(ptr, nextPollTimeoutMillis);
...
}
根據Android跨進程通信IPC之3——關于"JNI"的那些事中知道,nativeDestroy()這個native方法對應的是android_os_MessageQueue.cpp里面的android_os_MessageQueue_nativePollOnce()函數
1、nativePollOnce()
代碼在MessageQueue.java 188行
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, jlong ptr, jint timeoutMillis) {
//將Java層傳遞下來的mPtr轉換為nativeMessageQueue
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
我們看到上面代碼是
- 首先,將Java層傳遞下來的mPtr轉換為nativeMessageQueue
- 其次,nativeMessageQueue調用pollOnce(env, obj, timeoutMillis)
那我們就來看下pollOnce(env, obj, timeoutMillis)方法
2、 NativeMessageQueue::pollOnce(JNIEnv*, jobject, int)函數
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
// 重點函數
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
這個函數內容很簡答, 主要就是進行賦值,并調用pollOnce(timeoutMillis)
那我們再來看一下pollOnce(timeoutMillis)函數
3、Looper::pollOnce()函數
代碼在Looper.h 264 行
inline int pollOnce(int timeoutMillis) {
return pollOnce(timeoutMillis, NULL, NULL, NULL);
}
這個函數里面主要是調用的是ollOnce(timeoutMillis, NULL, NULL, NULL);
4、Looper::pollOnce(int, int, int, void**)函數
代碼在Looper.cpp 264 行
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
// 對fd對應的Responses進行處理,后面發現Response里都是活動fd
for (;;) {
// 先處理沒有Callback的Response事件
while (mResponseIndex < mResponses.size()) {
const Response& response = mResponses.itemAt(mResponseIndex++);
int ident = response.request.ident;
if (ident >= 0) {
// ident>=0則表示沒有callback,因為POLL_CALLBACK=-2
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - returning signalled identifier %d: "
"fd=%d, events=0x%x, data=%p",
this, ident, fd, events, data);
#endif
if (outFd != NULL) *outFd = fd;
if (outEvents != NULL) *outEvents = events;
if (outData != NULL) *outData = data;
return ident;
}
}
// 注意這里處于循環內部,改變result的值在后面的pollInner
if (result != 0) {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - returning result %d", this, result);
#endif
if (outFd != NULL) *outFd = 0;
if (outEvents != NULL) *outEvents = 0;
if (outData != NULL) *outData = NULL;
return result;
}
// 再處理內部輪訓
result = pollInner(timeoutMillis);
}
}
參數說明:
- timeoutMillis:超時時長
- outFd:發生事件的文件描述符
- outEvents:當前outFd上發生的事件,包含以下4類事件
- EVENT_INPUT:可讀
- EVENT_OUTPUT:可寫
- EVENT_ERROR:錯誤
- EVENT_HANGUP:中斷
- outData:上下文數據
這個函數內部最后調用了pollInner(int),讓我們來看一下
5、Looper::pollInner()函數
代碼在Looper.cpp 220 行
int Looper::pollInner(int timeoutMillis) {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
#endif
// Adjust the timeout based on when the next message is due.
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;
}
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - next message in %" PRId64 "ns, adjusted timeout: timeoutMillis=%d",
this, mNextMessageUptime - now, timeoutMillis);
#endif
}
// Poll.
int result = POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;
// We are about to idle.
// 即將處于idle狀態
mPolling = true;
// fd最大的個數是16
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
// 等待時間發生或者超時,在nativeWake()方法,向管道寫端寫入字符,則方法會返回。
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// No longer idling.
// 不再處于idle狀態
mPolling = false;
// 請求鎖 ,因為在Native Message的處理和添加邏輯上需要同步
// Acquire lock.
mLock.lock();
// Rebuild epoll set if needed.
// 如果需要,重建epoll
if (mEpollRebuildRequired) {
mEpollRebuildRequired = false;
// epoll重建,直接跳轉到Done
rebuildEpollLocked();
goto Done;
}
// Check for poll error.
if (eventCount < 0) {
if (errno == EINTR) {
goto Done;
}
ALOGW("Poll failed with an unexpected error, errno=%d", errno);
// epoll事件個數小于0,發生錯誤,直接跳轉Done
result = POLL_ERROR;
goto Done;
}
// Check for poll timeout.
//如果需要,重建epoll
if (eventCount == 0) {
//epoll事件個數等于0,發生超時,直接跳轉Done
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - timeout", this);
#endif
result = POLL_TIMEOUT;
goto Done;
}
// Handle all events.
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
#endif
// 循環處理所有的事件
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
//首先處理mWakeEventFd
if (fd == mWakeEventFd) {
//如果是喚醒mWakeEventFd有反應
if (epollEvents & EPOLLIN) {
/**重點代碼*/
// 已經喚醒了,則讀取并清空管道數據
awoken(); // 該函數內部就是read,從而使FD可讀狀態被清除
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
} else {
// 其他input fd處理,其實就是將活動放入response隊列,等待處理
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;
// 處理request,生成對應的response對象,push到響應數組
pushResponse(events, mRequests.valueAt(requestIndex));
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
"no longer registered.", epollEvents, fd);
}
}
}
Done: ;
// Invoke pending message callbacks.
// 再處理Native的Message,調用相應回調方法
mNextMessageUptime = LLONG_MAX;
while (mMessageEnvelopes.size() != 0) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
if (messageEnvelope.uptime <= now) {
// Remove the envelope from the list.
// We keep a strong reference to the handler until the call to handleMessage
// finishes. Then we drop it so that the handler can be deleted *before*
// we reacquire our lock.
{ // obtain handler
sp<MessageHandler> handler = messageEnvelope.handler;
Message message = messageEnvelope.message;
mMessageEnvelopes.removeAt(0);
mSendingMessage = true;
// 釋放鎖
mLock.unlock();
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
ALOGD("%p ~ pollOnce - sending message: handler=%p, what=%d",
this, handler.get(), message.what);
#endif
// 處理消息事件
handler->handleMessage(message);
} // 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();
// Invoke all response callbacks.
// 處理帶有Callback()方法的response事件,執行Response相應的回調方法
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;
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p",
this, response.request.callback.get(), fd, events, data);
#endif
// Invoke the callback. Note that the file descriptor may be closed by
// the callback (and potentially even reused) before the function returns so
// we need to be a little careful when removing the file descriptor afterwards.
// 處理請求的回調方法
int callbackResult = response.request.callback->handleEvent(fd, events, data);
if (callbackResult == 0) {
// 移除fd
removeFd(fd, response.request.seq);
}
// Clear the callback reference in the response structure promptly because we
// will not clear the response vector itself until the next poll.
// 清除response引用的回調方法
response.request.callback.clear();
// 發生回調
result = POLL_CALLBACK;
}
}
return result;
}
pollOnce返回值說明:
- POLL_WAKE: 表示由wake()出發,即pipe寫端的write事件觸發
- POLL_CALLBACK:表示某個被監聽fd被觸發
- POLL_TIMEOUT:表示等待超時
- POLL_ERROR:表示等待期間發生錯誤
pollInner()方法的處理流程:
- 1、先調用epoll_wait(),這是阻塞方法,用于等待事件發生或者超時。
- 2、對于epoll_wait()返回,當且僅當以下3種情況出現
- POLL_ERROR:發生錯誤,直接跳轉Done
- POLL_TIMEOUT:發生超時,直接跳轉到Done
- 檢測到管道有事情發生,則再根據情況做相應處理:
- 如果檢測到管道產生事件,則直接讀取管道的數據
- 如果是其他事件,則處理request,生成對應的response對象,push到response數組
- 3、進入Done標記位的代碼:
- 先處理Native的Message,調用Native的Handler來處理該Message
- 再處理Resposne數組,POLL_CALLBACK類型的事件
從上面的流程,可以發現對于Request先收集,一并放入response數組,而不是馬上執行。真正在Done開始執行的時候,先處理Native Message,再處理Request,說明Native Message優先級高于Request請求的優先級。
PS:在polOnce()方法中,先處理Response數組不帶Callback的事件,再調用了再調用了pollInner()函數。
6、Looper::awoken()函數
代碼在Looper.cpp 418行
void Looper::awoken() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ awoken", this);
#endif
uint64_t counter;
// 不斷的讀取管道數據,目的就是為了清空管道內容
TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t)));
}
7、小結
整體的流程圖如下:
(四)、nativeDestroy()
nativeWake用于喚醒功能,在添加消息到消息隊列enqueueMessage(),或者把消息從消息隊列中全部移除quit(),再有需要時會調用nativeWake方法。包含喚醒過程的添加消息的調用鏈
下面來進一步來看看調用鏈的過程:
1、enqueueMessage(Message, long)
代碼在MessageQueue.java 533行
boolean enqueueMessage(Message msg, long when) {
....
//將Message按按時間插入MessageQueue
if (needWake) {
nativeWake(mPtr);
}
....
}
在向消息隊列添加Message時,需要根據mBlocked情況來就決定是否需要調用nativeWake。
根據Android跨進程通信IPC之3——關于"JNI"的那些事中知道,nativeDestroy()這個native方法對應的是android_os_MessageQueue.cpp里面的android_os_MessageQueue_nativeWake(JNIEnv*, jclass, jlong ) 函數
2、android_os_MessageQueue_nativeWake()
代碼在android_os_MessageQueue.cpp 194行
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
// 將Java層傳遞下來的mPtr轉換為nativeMessageQueue
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
//調用wake函數
nativeMessageQueue->wake();
}
我們看到上面代碼是
- 首先,將Java層傳遞下來的mPtr轉換為nativeMessageQueue
- 其次,nativeMessageQueue調用wake()函數
3、NativeMessageQueue::wake()函數
代碼在android_os_MessageQueue.cpp 121行
void NativeMessageQueue::wake() {
mLooper->wake();
}
這個方法很簡單,就是直接調用Looper的wake()函數,
4、Looper::wake()函數
代碼在Looper.cpp 404行
void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ wake", this);
#endif
uint64_t inc = 1;
// 向管道mWakeEventFd寫入字符1
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
ALOGW("Could not write wake signal, errno=%d", errno);
}
}
}
Looper類的 wake()函數只是往mWakeEventfd中寫了一些內容,這個fd只是通知而已,類似于pipi,最后會把epoll_wai喚醒,線程就不阻塞了繼續發送
Native層的消息,然后處理之前的addFd事件,然后處理Java層的消息。
PS:其中TEMP_FAILURE_RETRY 是一個宏定義,當執行write失敗后,會不斷重復執行,直到執行成功為止。
5、小結
總結一下流程圖如下:
(五)、sendMessage()
前面幾篇文章講述了Java層如何向MessageQueue類添加消息,那么接下來講講Native層如何向MessageQueue發送消息。
1、Looper::sendMessage(const sp<MessageHandler>& handler, const Message& message) 函數
代碼在Looper.cpp 583行
void Looper::sendMessage(const sp<MessageHandler>& handler, const Message& message) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
sendMessageAtTime(now, handler, message);
}
我們看到方法里面調用了sendMessageAtTime(now, handler, message) 函數
2、 Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,
const Message& message)函數
代碼在Looper.cpp 588行
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);
}
我們看到方法里面調用了sendMessageAtTime(now, handler, message) 函數
所以我們說:
sendMessage()和sendMessageDelayed()都是調用sendMessageAtTime()來完成消息插入。
那我們就來看一下sendMessageAtTime()
3、 Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,
const Message& 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();
// 找到message應該插入的位置i
while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) {
i += 1;
}
MessageEnvelope messageEnvelope(uptime, handler, message);
mMessageEnvelopes.insertAt(messageEnvelope, i, 1);
// Optimization: If the Looper is currently sending a message, then we can skip
// the call to wake() because the next thing the Looper will do after processing
// messages is to decide when the next wakeup time should be. In fact, it does
// not even matter whether this code is running on the Looper thread.
// 如果當前正在發送消息,那么不再調用wake(),直接返回
if (mSendingMessage) {
return;
}
} // release lock
// 釋放鎖
// Wake the poll loop only when we enqueue a new message at the head.
// 當消息加入到消息隊列的頭部時,需要喚醒poll循環
if (i == 0) {
wake();
}
}
(六)、sendMessage()
本節介紹了MessageQueue的native()方法,經過層層調用:
- nativeInit()方法,最終實現由epoll機制中的epoll_create()/epoll_ctl()完成
- nativeDestory()方法,最終實現由RefBase::decStrong()完成
- nativePollOnce()方法,最終實現由Looper::pollOnce()完成
- nativeWake()方法,最終實現由Looper::wake()調用write方法,向管道寫入字符
- nativeIsPolling(),nativeSetFileDescriptorEvents()這兩個方法類似,此處就不一一列舉了。
三、Native結構體和類
Looper.h/Looper.cpp文件中定義了Message結構體,消息處理類,回調類,Looper類
(一)、Message結構體
代碼在(http://androidxref.com/6.0.1_r10/xref/system/core/include/utils/Looper.h) 50行
struct Message {
Message() : what(0) { }
Message(int what) : what(what) { }
/* The message type. (interpretation is left up to the handler) */
// 消息類型
int what;
};
(二)、消息處理類
1、MessageHandler類
代碼在Looper.h 67行
/**
* Interface for a Looper message handler.
*
* The Looper holds a strong reference to the message handler whenever it has
* a message to deliver to it. Make sure to call Looper::removeMessages
* to remove any pending messages destined for the handler so that the handler
* can be destroyed.
*/
class MessageHandler : public virtual RefBase {
protected:
virtual ~MessageHandler() { }
public:
/**
* Handles a message.
*/
virtual void handleMessage(const Message& message) = 0;
};
這個類很簡單,就不多說了,這里說下注釋:
- 處理Looper消息程序的接口。
- 當一個消息要傳遞給其對應的Handler時候,Looper持有一個消息Handler的強引用。在這個Handler銷毀之前,請確保調用Looper :: removeMessages來刪除待處理的消息。
2、WeakMessageHandler類
代碼在Looper.h 82行
/**
* A simple proxy that holds a weak reference to a message handler.
*/
class WeakMessageHandler : public MessageHandler {
protected:
virtual ~WeakMessageHandler();
public:
WeakMessageHandler(const wp<MessageHandler>& handler);
virtual void handleMessage(const Message& message);
private:
wp<MessageHandler> mHandler;
};
這里并沒有handleMessage的代碼,我們是不是忽略了什么?再找一下,果然這塊的代碼在
Looper.cpp 38行
void WeakMessageHandler::handleMessage(const Message& message) {
sp<MessageHandler> handler = mHandler.promote();
if (handler != NULL) {
調用Mes
handler->handleMessage(message);
}
}
(三)、回調類
1、LooperCallback類
代碼在Looper.h 98行
/**
* A looper callback.
*/
class LooperCallback : public virtual RefBase {
protected:
virtual ~LooperCallback() { }
public:
/**
* Handles a poll event for the given file descriptor.
* It is given the file descriptor it is associated with,
* a bitmask of the poll events that were triggered (typically EVENT_INPUT),
* and the data pointer that was originally supplied.
*
* Implementations should return 1 to continue receiving callbacks, or 0
* to have this file descriptor and callback unregistered from the looper.
*/
// 用于處理指定的文件描述符poll事件
virtual int handleEvent(int fd, int events, void* data) = 0;
};
簡單翻譯一下handleEvent方法的注釋:
- 處理給定文件描述符的輪訓事件。
- 用來 將 最初提供的數據指針和輪訓事件的掩碼(通常為EVENT_INPUT)來關聯的文件描述符。
- 實現子類如果想繼續接收回調則返回1,如果未注冊文件描述符和回調則返回0
2、SimpleLooperCallback類
代碼在Looper.cpp 118行
class SimpleLooperCallback : public LooperCallback {
protected:
virtual ~SimpleLooperCallback();
public:
SimpleLooperCallback(Looper_callbackFunc callback);
virtual int handleEvent(int fd, int events, void* data);
private:
Looper_callbackFunc mCallback;
};
它和WeakMessageHandler類一樣handleEvent的方法在Looper.cpp 55行
int SimpleLooperCallback::handleEvent(int fd, int events, void* data) {
// 調用回調方法
return mCallback(fd, events, data);
}
(四)、Looper類
1 、 Native層的Looper類簡介
2 、 Native層的Looper類常量
// 每個epoll實例默認的文件描述符個數
static const int EPOLL_SIZE_HINT = 8;
// 輪訓事件的文件描述符個數上限
static const int EPOLL_MAX_EVENTS = 16;
3、Native Looper類的常用方法:
方法 | 解釋 |
---|---|
Looper(bool) | Looper的構造函數 |
static sp<Looper> prepar(int) | 如果該線程沒有綁定Looper,才創建Loopr,否則直接返回 |
int pollOnec(int ,int* int,void) | 輪訓,等待事件發生 |
void wake() | 喚醒Looper |
void sendMessage(const sp<MessageHandler>&handler,const Message&message) | 發送消息 |
int addFd(int,int,int,Looper_callbackFunc,void*) | 添加要監聽的文件描述符fd |
4、Request、Resposne、MessageEvent 三個結構體
Looper類的內部定義了Request、Resposne、MessageEnvelope這三個結構體
關系圖如下:
4.1、Request 結構體
代碼在Looper.h 420行
// 請求結構體
struct Request {
int fd;
int ident;
int events;
int seq;
sp<LooperCallback> callback;
void* data;
void initEventItem(struct epoll_event* eventItem) const;
};
4.2、Resposne 結構體
代碼在Looper.h 431行
// 響應結構體
struct Response {
int events;
Request request;
};
4.3、MessageEnvelope 結構體
代碼在Looper.h 436行
// 信封結構體
struct MessageEnvelope {
MessageEnvelope() : uptime(0) { }
MessageEnvelope(nsecs_t uptime, const sp<MessageHandler> handler,
const Message& message) : uptime(uptime), handler(handler), message(message) {
}
nsecs_t uptime;
sp<MessageHandler> handler;
Message message;
};
MessageEnvelope正如其名字,信封。MessageEnvelope里面記錄著收信人(handler),發信時間(uptime),信件內容(message)。
5、Native Looper類的類圖如下:
6 Native Looper的監聽文件描述符
Native Looper除了提供message機制外,還提供監聽文件描述符的方式。通過addFd()接口加入需要被監聽的文件描述符。
代碼在Looper.cpp 434行
int addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data);
int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data);
其中:
- fd:為所需要監聽的文件描述符
- ident:表示為當前發生時間的標識符,必須>=0,或者為POLL_CALLBACK(-2)如果指定了callback
- events:表示為要監聽的文件類型,默認是EVENT_INPUT。
- callback:當有事件發生時,會回調該callback函數。
- data:兩種使用方式:
- 指定callback來處理事件:當該文件描述符上有事件來時,該callback會被執行,然后從fd讀取數據。這個時候ident是被忽略的。
- 通過指定的ident來處理事件:當該文件描述符有數據來到時,pollOnce()會返回一個ident,調用者會判斷該ident是否等于自己需要處理事件ident,如果是的話,則開始處理事件。
(####) 五、Java層的addFd
我之前一直以為只能在C層的Looper中才能addFd,原來在Java層也通過JNI做了這個功能。我們可以在MessageQueue中的addOnFileDescriptorEventListener來實現這個功能。
代碼在MessageQueue.java 186行
/**
* Adds a file descriptor listener to receive notification when file descriptor
* related events occur.
* <p>
* If the file descriptor has already been registered, the specified events
* and listener will replace any that were previously associated with it.
* It is not possible to set more than one listener per file descriptor.
* </p><p>
* It is important to always unregister the listener when the file descriptor
* is no longer of use.
* </p>
*
* @param fd The file descriptor for which a listener will be registered.
* @param events The set of events to receive: a combination of the
* {@link OnFileDescriptorEventListener#EVENT_INPUT},
* {@link OnFileDescriptorEventListener#EVENT_OUTPUT}, and
* {@link OnFileDescriptorEventListener#EVENT_ERROR} event masks. If the requested
* set of events is zero, then the listener is unregistered.
* @param listener The listener to invoke when file descriptor events occur.
*
* @see OnFileDescriptorEventListener
* @see #removeOnFileDescriptorEventListener
*/
public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd,
@OnFileDescriptorEventListener.Events int events,
@NonNull OnFileDescriptorEventListener listener) {
if (fd == null) {
throw new IllegalArgumentException("fd must not be null");
}
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
}
synchronized (this) {
updateOnFileDescriptorEventListenerLocked(fd, events, listener);
}
}
通過上面代碼分析,我們知道這里面有兩個重點
- 1 onFileDescriptorEventListener 這個回調
- 2 updateOnFileDescriptorEventListenerLocked()方法
8.1、OnFileDescriptorEventListener
代碼在MessageQueue.java 186行
/**
* A listener which is invoked when file descriptor related events occur.
*/
public interface OnFileDescriptorEventListener {
/**
* File descriptor event: Indicates that the file descriptor is ready for input
* operations, such as reading.
* <p>
* The listener should read all available data from the file descriptor
* then return <code>true</code> to keep the listener active or <code>false</code>
* to remove the listener.
* </p><p>
* In the case of a socket, this event may be generated to indicate
* that there is at least one incoming connection that the listener
* should accept.
* </p><p>
* This event will only be generated if the {@link #EVENT_INPUT} event mask was
* specified when the listener was added.
* </p>
*/
public static final int EVENT_INPUT = 1 << 0;
/**
* File descriptor event: Indicates that the file descriptor is ready for output
* operations, such as writing.
* <p>
* The listener should write as much data as it needs. If it could not
* write everything at once, then it should return <code>true</code> to
* keep the listener active. Otherwise, it should return <code>false</code>
* to remove the listener then re-register it later when it needs to write
* something else.
* </p><p>
* This event will only be generated if the {@link #EVENT_OUTPUT} event mask was
* specified when the listener was added.
* </p>
*/
public static final int EVENT_OUTPUT = 1 << 1;
/**
* File descriptor event: Indicates that the file descriptor encountered a
* fatal error.
* <p>
* File descriptor errors can occur for various reasons. One common error
* is when the remote peer of a socket or pipe closes its end of the connection.
* </p><p>
* This event may be generated at any time regardless of whether the
* {@link #EVENT_ERROR} event mask was specified when the listener was added.
* </p>
*/
public static final int EVENT_ERROR = 1 << 2;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag=true, value={EVENT_INPUT, EVENT_OUTPUT, EVENT_ERROR})
public @interface Events {}
/**
* Called when a file descriptor receives events.
*
* @param fd The file descriptor.
* @param events The set of events that occurred: a combination of the
* {@link #EVENT_INPUT}, {@link #EVENT_OUTPUT}, and {@link #EVENT_ERROR} event masks.
* @return The new set of events to watch, or 0 to unregister the listener.
*
* @see #EVENT_INPUT
* @see #EVENT_OUTPUT
* @see #EVENT_ERROR
*/
@Events int onFileDescriptorEvents(@NonNull FileDescriptor fd, @Events int events);
}
private static final class FileDescriptorRecord {
public final FileDescriptor mDescriptor;
public int mEvents;
public OnFileDescriptorEventListener mListener;
public int mSeq;
public FileDescriptorRecord(FileDescriptor descriptor,
int events, OnFileDescriptorEventListener listener) {
mDescriptor = descriptor;
mEvents = events;
mListener = listener;
}
}
8.2、updateOnFileDescriptorEventListenerLocked()方法
代碼在MessageQueue.java 222行
private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events,
OnFileDescriptorEventListener listener) {
final int fdNum = fd.getInt$();
int index = -1;
FileDescriptorRecord record = null;
if (mFileDescriptorRecords != null) {
index = mFileDescriptorRecords.indexOfKey(fdNum);
if (index >= 0) {
record = mFileDescriptorRecords.valueAt(index);
if (record != null && record.mEvents == events) {
return;
}
}
}
if (events != 0) {
events |= OnFileDescriptorEventListener.EVENT_ERROR;
if (record == null) {
if (mFileDescriptorRecords == null) {
mFileDescriptorRecords = new SparseArray<FileDescriptorRecord>();
}
//fd保存在FileDescriptorRecord對象
record = new FileDescriptorRecord(fd, events, listener);
// mFileDescriptorRecords 保存
mFileDescriptorRecords.put(fdNum, record);
} else {
record.mListener = listener;
record.mEvents = events;
record.mSeq += 1;
}
// 調用native函數
nativeSetFileDescriptorEvents(mPtr, fdNum, events);
} else if (record != null) {
record.mEvents = 0;
mFileDescriptorRecords.removeAt(index);
}
}
8.2.1、android_os_MessageQueue_nativeSetFileDescriptorEvents()函數
根據Android跨進程通信IPC之3——關于"JNI"的那些事中知道,nativeInit這個native方法對應的是android_os_MessageQueue.cpp里面的android_os_MessageQueue_nativeSetFileDescriptorEvents(JNIEnv* env, jclass clazz, jlong ptr, jint fd, jint events)函數
代碼在android_os_MessageQueue.cpp 204行
static void android_os_MessageQueue_nativeSetFileDescriptorEvents(JNIEnv* env, jclass clazz,
jlong ptr, jint fd, jint events) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->setFileDescriptorEvents(fd, events);
}
我們看到這個函數里面調用了nativeMessageQueue的setFileDescriptorEvents(fd, events);函數。
8.2.2、NativeMessageQueue::setFileDescriptorEvents(int fd, int events)函數
代碼在android_os_MessageQueue.cpp 125行
void NativeMessageQueue::setFileDescriptorEvents(int fd, int events) {
if (events) {
int looperEvents = 0;
if (events & CALLBACK_EVENT_INPUT) {
looperEvents |= Looper::EVENT_INPUT;
}
if (events & CALLBACK_EVENT_OUTPUT) {
looperEvents |= Looper::EVENT_OUTPUT;
}
// 重點代碼
mLooper->addFd(fd, Looper::POLL_CALLBACK, looperEvents, this,
reinterpret_cast<void*>(events));
} else {
mLooper->removeFd(fd);
}
}
我們看到了在這個函數內部調用了mLooper的addFd函數。
大家注意一下Looper的addFd函數,中的倒數二個參數是this,側面說明了NativeMessageQueue繼承了LooperCallback。
代碼在android_os_MessageQueue.cpp 41行
class NativeMessageQueue : public MessageQueue, public LooperCallback {
public:
NativeMessageQueue();
virtual ~NativeMessageQueue();
virtual void raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj);
void pollOnce(JNIEnv* env, jobject obj, int timeoutMillis);
void wake();
void setFileDescriptorEvents(int fd, int events);
virtual int handleEvent(int fd, int events, void* data);
...
}
所以說,需要實現handleEvent()函數。handleEvent()函數就是在looper中epoll_wait之后,當我們增加的fd有數據就會調用這個函數。
代碼在android_os_MessageQueue.cpp 141行
int NativeMessageQueue::handleEvent(int fd, int looperEvents, void* data) {
int events = 0;
if (looperEvents & Looper::EVENT_INPUT) {
events |= CALLBACK_EVENT_INPUT;
}
if (looperEvents & Looper::EVENT_OUTPUT) {
events |= CALLBACK_EVENT_OUTPUT;
}
if (looperEvents & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP | Looper::EVENT_INVALID)) {
events |= CALLBACK_EVENT_ERROR;
}
int oldWatchedEvents = reinterpret_cast<intptr_t>(data);
// 調用回調
int newWatchedEvents = mPollEnv->CallIntMethod(mPollObj,
gMessageQueueClassInfo.dispatchEvents, fd, events); /
if (!newWatchedEvents) {
return 0; // unregister the fd
}
if (newWatchedEvents != oldWatchedEvents) {
setFileDescriptorEvents(fd, newWatchedEvents);
}
return 1;
}
最后在java的MessageQueue中的dispatchEvent就是在jni層反調過來的,然后調用之前注冊的回調函數
代碼在MessageQueue.java259行
// Called from native code.
private int dispatchEvents(int fd, int events) {
// Get the file descriptor record and any state that might change.
final FileDescriptorRecord record;
final int oldWatchedEvents;
final OnFileDescriptorEventListener listener;
final int seq;
synchronized (this) {
record = mFileDescriptorRecords.get(fd);
if (record == null) {
return 0; // spurious, no listener registered
}
oldWatchedEvents = record.mEvents;
events &= oldWatchedEvents; // filter events based on current watched set
if (events == 0) {
return oldWatchedEvents; // spurious, watched events changed
}
listener = record.mListener;
seq = record.mSeq;
}
// Invoke the listener outside of the lock.
int newWatchedEvents = listener.onFileDescriptorEvents(
record.mDescriptor, events);
if (newWatchedEvents != 0) {
newWatchedEvents |= OnFileDescriptorEventListener.EVENT_ERROR;
}
// Update the file descriptor record if the listener changed the set of
// events to watch and the listener itself hasn't been updated since.
if (newWatchedEvents != oldWatchedEvents) {
synchronized (this) {
int index = mFileDescriptorRecords.indexOfKey(fd);
if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record
&& record.mSeq == seq) {
record.mEvents = newWatchedEvents;
if (newWatchedEvents == 0) {
mFileDescriptorRecords.removeAt(index);
}
}
}
}
// Return the new set of events to watch for native code to take care of.
return newWatchedEvents;
}
四、總結
(一)、Native與Java的對應關系
MessageQueue通過mPtr變量保存了NativeMessageQueue對象,從而使得MessageQueue成為Java層和Native層的樞紐,既能處理上層消息,也能處理Native消息,下圖列舉了Java層與Native層的對應圖
圖解:
- 1、紅色虛線關系:Java層和Native層的MessageQueue通過JNI建立關聯,彼此之間能相互調用,搞明白這個互調關系,也就搞明白Java如何調用C++代碼,C++代碼如何調用Java代碼
- 2、藍色虛線關系:Handler/Looper/Message這三大類Java層與Native層并沒有任何真正的關系,只是分別在Java層和Native層的handler消息模型中具有相似的功能。都是彼此獨立的,各自實現相應的邏輯。
- 3、WeakMessageHandler繼承與MessageHandler類,NativeMessageQueue繼承于MessageQueue類。
另外,消息處理流程是先處理NativeMessage,再處理Native Request,最后處理Java Message。理解了該流程也就明白了有時上層消息很少,但響應時間卻比較長的真正原因。
(二)、Native的流程
整體流程如下:
四 總結
Handler機制中Native的實現主要涉及了兩個類
- 1、NativeMessageQueue:在MessageQueue.java的構造函數中,調用了nativeInit創建了NativeMessageQueue對象,并且把指針變量返回給Java層的mPtr。而在NativeMessageQueue的構造函數中,會在當前線程中創建C++的Looper對象。
- 2、Looper:控制eventfd的讀寫,通過epoll監聽eventfd的變化,來阻塞調用pollOnce和恢復調用wake當前線程
- 通過 epoll監聽其他文件描述符的變化
- 通過 epoll處理C++層的消息機制,當調用Looper::sendMessageAtTime后,調用wake觸發epoll
- Looper的構造函數,創建一個eventfd(以前版本是pipe),eventfd它的主要用于進程或者線程間的通信,然后創建epoll來監聽該eventfd的變化
- Looper::pollOnce(int timeoutMillis) 內部調用了pollInner,再調用epoll_wait(mEpollFd, ..., timeoutMillis)阻塞timeoutMills時間,并監聽文件描述符mEpollFd的變化,當時間到了或者消息到了,即eventfd被寫入內容后,從epoll_wait繼續往下執行,處理epoll_wait返回的消息,該消息既有可能是eventfd產生的,也可能是其他文件描述符產生的。處理順序是,先處理普通的C++消息隊列mMessageEnvelopes,然后處理之前addFd的事件,最后從pollOnce返回,會繼續MessageQueue.java的next()函數,取得Java層的消息來處理;
- Looper類的wake,函數只是往mWakeEventfd中寫了一些內容,這個fd只是通知而已,類似pipe,最后會把epoll_wait喚醒,線程就不阻塞了,繼續先發送C層消息,然后處理之前addFd事件,然后處理Java層消息