Android通信方式篇(三)-消息機制(Native層)

在前面介紹的Java層中,我們看到了MessageQueue有若干native方法,想必肯定與native層有關(guān),但其實Native層本身就有一套完整的消息機制。另外,在整個消息機制中,MessageQueue它是連接Java層和Native層的紐帶,兩層都可以向其消息隊列中添加消息。

那么還是接著MessageQueue初始化開始分析,之前了解到,MessageQueue初始化會執(zhí)行nativeInit():

//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);
    return reinterpret_cast<jlong>(nativeMessageQueue);
}

創(chuàng)建了NativeMessageQueue

//android_os_MessageQueue.cpp
NativeMessageQueue::NativeMessageQueue() :
        mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false);
        Looper::setForThread(mLooper);
    }
}

然后在NativeMessageQueue初始化過程中,又創(chuàng)建了native Looper

//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);
    LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd: %s",
                        strerror(errno));
    AutoMutex _l(mLock);
    rebuildEpollLocked();
}

這里創(chuàng)建了一個eventfd,代碼來自7.0,這部分和5.0 pipe管道的mWakeReadPipeFd和mWakeWritePipeFd稍微有點不一樣,前者是等待/響應(yīng),后者是讀取/寫入。

接著重點看下rebuildEpollLocked()方法:

void Looper::rebuildEpollLocked() {
...
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
    struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event));
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mWakeEventFd;
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
      //最后三行往mEpollFd代理中、注冊、一個叫mWakeEventFd流、的數(shù)據(jù)流入事件(EPOLLIN)。
   ...
}

通過epoll_create創(chuàng)建了一個epoll專用的文件描述符,EPOLL_SIZE_HINT表示mEpollFd上能監(jiān)控的最大文件描述符數(shù)。最后調(diào)用epoll_ctl監(jiān)控mWakeEventFd文件描述符的Epoll事件,即當(dāng)mWakeEventFd中有內(nèi)容可讀時,就喚醒當(dāng)前正在等待的線程.。這里簡單說就是Android native層用了Epoll模型。那什么是Epoll模型呢?

首先解釋下為什么要引入這個Epoll模型,簡單講,它就是實現(xiàn)queue.next 在無消息時候的阻塞。Lopper肯定不會做啥工作都沒干的死循環(huán)。這也就是為什么Android中主線程不會因為Looper.loop()里的死循環(huán)卡死。

Epoll簡單介紹

傳統(tǒng)的阻塞型I/O(一邊寫,一邊讀),一個線程只能處理一個一個IO流。如果一個線程想要處理多個流,可以采用了非阻塞、輪詢I/O方式,但是傳統(tǒng)的非阻塞處理多個流的時候,會遍歷所有流,但是如果所有流都沒數(shù)據(jù),就會白白浪費CPU。于是出現(xiàn)了select、poll、epoll三種常見的代理方式。

select,poll,epoll都是IO多路復(fù)用的機制。I/O多路復(fù)用就通過一種機制,可以監(jiān)視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進(jìn)行相應(yīng)的讀寫操作。select,poll,epoll本質(zhì)上都是同步I/O,因為他們都需要在讀寫事件就緒后自己負(fù)責(zé)進(jìn)行讀寫,也就是說這個讀寫過程是阻塞的。

  • select、poll :簡單說就是無差別輪詢的代理方式。

  • epoll: 比select、poll高效,可以理解為Event poll,代理者會代理流的時候也伴隨著事件,因此有了對應(yīng)事件,就可以避免無差別輪詢了。其通常的操作有:epoll_create(創(chuàng)建一個epoll)、epoll_ctl(往epoll中增加/刪除某一個流的某一個事件)、epoll_wait(在一定時間內(nèi)等待事件的發(fā)生)。

接著我們來分析下Native層消息的發(fā)送和獲取。其實native層更多的是負(fù)責(zé)消息的調(diào)度,比如說何時阻塞、何時喚醒線程,避免CPU浪費。

發(fā)送消息

發(fā)送消息在native比較簡單,handler發(fā)送消息后,會到MessageQueue的enqueueMessage,此時在線程阻塞的情況下,會調(diào)用nativeWake來喚起線程。

//android_os_MessageQueue.cpp
void NativeMessageQueue::wake() {
   mLooper->wake();
}

對應(yīng)wake方法:

//Looper.cpp
void Looper::wake() {
   ...
   uint64_t inc = 1;
   ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
   if (nWrite != sizeof(uint64_t)) {
 ...
   }
}

這里TEMP_FAILURE_RETRY是一個宏定義,顧名思義,就是不斷地嘗試往mWakeEventFd流里面寫一個無用數(shù)據(jù)直到成功,以此來喚醒queue.next。

提取消息

也就是Java層Looper 死循環(huán)中的queue.next

//MessageQueue.java
Message next() {
       ...
       for (;;) {
          ...
           nativePollOnce(ptr, nextPollTimeoutMillis);
          ...
       }
   }

對于Native:

//android_os_MessageQueue.cpp
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);
}

繼續(xù)往下看

void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
   ...
   mLooper->pollOnce(timeoutMillis);
   ...
}
//Looper.cpp
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    for (;;) {
        while (mResponseIndex < mResponses.size()) {
        ...
        result = pollInner(timeoutMillis);
    }
}

最后到了:

int Looper::pollInner(int timeoutMillis) {
   ...
   int result = POLL_WAKE;
   mPolling = true;
   struct epoll_event eventItems[EPOLL_MAX_EVENTS];
   int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
   mPolling = false;
   mLock.lock();
   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();
           } else {
               ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
           }
       }
       ...
   }
   ...
   mLock.unlock();
   ...
   return result;
}

在這里,我們注意到epoll_wait方法,這里會得到一段時間內(nèi)(結(jié)合消息計算得來的)收到的事件個數(shù),這里對于queue來說就是空閑(阻塞)狀態(tài)。過了這個時間后,看看事件數(shù),如果為0,則意味著超時。否則,遍歷所有的事件,看看有沒有mWakeEventFd,且是EPOLLIN事件的,有的話就真正喚醒線程、解除空閑狀態(tài)。

最后是兩張關(guān)系圖:

1.兩層類對應(yīng)關(guān)系圖:

  • 紅色虛線關(guān)系:Java層和Native層的MessageQueue通過JNI建立關(guān)聯(lián),彼此之間能相互調(diào)用,搞明白這個互調(diào)關(guān)系,也就搞明白了Java如何調(diào)用C++代碼,C++代碼又是如何調(diào)用Java代碼。

  • 藍(lán)色虛線關(guān)系:Handler/Looper/Message這三大類Java層與Native層并沒有任何的真正關(guān)聯(lián),只是分別在Java層和Native層的handler消息模型中具有相似的功能。都是彼此獨立的,各自實現(xiàn)相應(yīng)的邏輯。

  1. 兩層功能模型:

參考:
https://www.cnblogs.com/qcloud1001/p/7993756.html
http://gityuan.com/2015/12/27/handler-message-native/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容