handler機(jī)制--讓線程變?yōu)椤坝绖?dòng)機(jī)”

handler要想能工作起來(lái),第一步要做的事情是讓線程變?yōu)椤坝绖?dòng)機(jī)”,也就是讓線程一直循環(huán)起來(lái),不死掉,這樣線程就可以不斷的處理各種任務(wù)了。那這節(jié)就來(lái)介紹下如何讓線程變?yōu)椤坝绖?dòng)機(jī)”。

如何讓線程變?yōu)椤坝绖?dòng)機(jī)”

下面代碼可以做到

public class Thread{

    public void run(){
        Looper.prepare();

        Looper.loop();
    }
}

如上代碼,需要依次調(diào)用Looper.prepare()和Looper.loop()方法就可以讓線程變?yōu)椤坝绖?dòng)機(jī)”,是不是非常的簡(jiǎn)單,那我們就從源碼角度一趟究竟。

線程變?yōu)椤坝绖?dòng)機(jī)”-源碼分析

前置知識(shí)

fd:Linux系統(tǒng)中把一切都看做是文件,當(dāng)進(jìn)程打開(kāi)現(xiàn)有文件或創(chuàng)建新文件時(shí),內(nèi)核向進(jìn)程返回一個(gè)文件描述符,文件描述符就是內(nèi)核為了高效管理已被打開(kāi)的文件所創(chuàng)建的索引,用來(lái)指向被打開(kāi)的文件,所有執(zhí)行 I/O 操作的系統(tǒng)調(diào)用都會(huì)通過(guò)文件描述符,文件描述符不一定是文件,也可以是一塊匿名內(nèi)存。
epoll:io多路復(fù)用技術(shù),就是在一個(gè)線程或進(jìn)程中監(jiān)聽(tīng)多個(gè)文件描述符是否可以執(zhí)行io操作的能力,handler使用了這種技術(shù),在下面會(huì)詳細(xì)介紹
eventfd:類似于管道的概念,可以實(shí)現(xiàn)線程間的事件通知 eventfd原文
ThreadLocal:這個(gè)類的主要作用是保存當(dāng)前線程獨(dú)有的數(shù)據(jù)。

相關(guān)類介紹

下面的文件屬于android s

frameworks/base/core/java/android/os/Looper.java
frameworks/base/core/java/android/os/MessageQueue.java
libcore/ojluni/annotations/hiddenapi/java/lang/ThreadLocal.java

frameworks/base/core/jni/android_os_MessageQueue.cpp
system/core/libutils/Looper.cpp

那我們就分別從:Looper.prepare()和Looper.loop()這兩個(gè)方法作為分析起點(diǎn)來(lái)進(jìn)行分析

1. Looper.prepare()

    public static void prepare() {
        //調(diào)用prepare方法,quitAllowed值為true,代表允許結(jié)束loop
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        //當(dāng)前線程已經(jīng)存在Looper,則不能再次創(chuàng)建,拋異常
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //sThreadLocal是一個(gè)ThreadLocal類型的靜態(tài)變量,它存儲(chǔ)Looper實(shí)例
        [1.1]
        sThreadLocal.set(new Looper(quitAllowed));
    }

sThreadLocal.set(new Looper(quitAllowed)) 這行代碼的作用是把Looper實(shí)例存儲(chǔ)在ThreadLocal中,存儲(chǔ)下來(lái)的一個(gè)最大的目的就是:能更方便的獲取當(dāng)前Thread對(duì)應(yīng)的Looper。比如可以在代碼的任何位置,只要調(diào)用Looper.myLooper()方法就能非常方便的獲取到Looper對(duì)象。

1.1 Looper#Looper()

    //Looper的構(gòu)造方法是私有的,只能調(diào)用Looper的靜態(tài)方法來(lái)創(chuàng)建Looper
    private Looper(boolean quitAllowed) {
        //new MessageQueue
        [1.2]
        mQueue = new MessageQueue(quitAllowed);
        //保存當(dāng)前線程引用
        mThread = Thread.currentThread();
    }

1.2 MessageQueue#MessageQueue()

    MessageQueue(boolean quitAllowed) {
        //quitAllowed的值當(dāng)前為true
        mQuitAllowed = quitAllowed;
        //這一看就是進(jìn)入nativeInit jni方法,該方法返回一個(gè)指針,mPtr保存下來(lái)
        [1.3]
        mPtr = nativeInit();
    }

1.3 android_os_MessageQueue.cpp#android_os_MessageQueue_nativeInit

    static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
        //new一個(gè)NativeMessageQueue
        [1.4]
        NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
        if (!nativeMessageQueue) {
            jniThrowRuntimeException(env, "Unable to allocate native queue");
            return 0;
        }

        //增加它的引用計(jì)數(shù)器
        nativeMessageQueue->incStrong(env);
        //把nativeMessageQueue指針轉(zhuǎn)化為jlong類型,返回給java層,
        return reinterpret_cast<jlong>(nativeMessageQueue);
    }

1.4 android_os_MessageQueue.cpp#NativeMessageQueue

    NativeMessageQueue::NativeMessageQueue() :
            mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
        //先獲取當(dāng)前線程的Looper,不存在則創(chuàng)建,native層也存在一個(gè)Looper
        mLooper = Looper::getForThread();
        if (mLooper == NULL) {
            //new 一個(gè)Looper出來(lái)
            [1.5]
            mLooper = new Looper(false);
            Looper::setForThread(mLooper);
        }
    }

1.5 Looper.cpp#Looper

    Looper::Looper(bool allowNonCallbacks)
        : mAllowNonCallbacks(allowNonCallbacks),
          mSendingMessage(false),
          mPolling(false),
          mEpollRebuildRequired(false),
          mNextRequestSeq(WAKE_EVENT_FD_SEQ + 1),
          mResponseIndex(0),
          mNextMessageUptime(LLONG_MAX) {
        //eventfd方法返回fd,EFD_NONBLOCK的作用在調(diào)用read/write函數(shù)的時(shí)候 不阻塞,EFD_CLOEXEC作用是在fork子進(jìn)程時(shí)候調(diào)用exec()方法的時(shí),把fd close調(diào),這樣在子進(jìn)程就不存在相應(yīng)的fd了
        mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
        LOG_ALWAYS_FATAL_IF(mWakeEventFd.get() < 0, "Could not make wake event fd: %s", strerror(errno));

        AutoMutex _l(mLock);
        rebuildEpollLocked();
    }

該方法中有幾個(gè)關(guān)鍵點(diǎn)需要介紹下:
eventfd:可以實(shí)現(xiàn)線程之間或者進(jìn)程之間通信,eventfd類似于pipe,但是比 pipe 更高效,一方面它比 pipe 少用一個(gè)fd,節(jié)省了資源;另一方面,eventfd 的緩沖區(qū)管理也簡(jiǎn)單得多,全部“buffer”一共只有8字節(jié)。eventfd在通信時(shí)候不能傳大數(shù)據(jù)。
eventfd是如何實(shí)現(xiàn)線程之間的通信,通過(guò)下面的偽代碼來(lái)說(shuō)明

    //用來(lái)保持生成的fd
    savedEventfd

    //調(diào)用eventfd方法,返回的fd是阻塞類型的
    savedEventfd = eventfd(0, EFD_CLOEXEC);

    //下面方法發(fā)生于線程A中,調(diào)用read方法從savedEventfd中讀取int數(shù)據(jù),因?yàn)閟avedEventfd是阻塞類型的,因此線程A會(huì)阻塞于read方法,直到有數(shù)據(jù)為止
    uint64_t counter;
    read(savedEventfd,&counter,sizeof(uint64_t));

    //下面方法調(diào)用發(fā)生于線程B中,調(diào)用write方法往savedEventfd中寫(xiě)一個(gè)int值,因?yàn)閟avedEventfd寫(xiě)入了數(shù)據(jù),上面線程A就會(huì)被喚醒,讀出剛剛寫(xiě)入的int數(shù)據(jù)
    uint64_t inc = 1;
    write(savedEventfd,&inc,sizeof(uint64_t));

mWakeEventFd:它保存了eventfd方法返回的fd,并且需要注意,在調(diào)用eventfd方法的時(shí)候,傳遞了EFD_NONBLOCK這個(gè)參數(shù),表示返回的fd是非阻塞類型,即調(diào)用read,write方法不會(huì)發(fā)生阻塞,上面的偽代碼是阻塞的,現(xiàn)在卻是非阻塞類型的,那又怎么實(shí)現(xiàn)線程之間或者進(jìn)程之間通信呢?答案是結(jié)合epoll機(jī)制。
mWakeEventFd的主要作用是java層的MessageQueue的喚醒/等待操作,喚醒/等待操作都是通過(guò)給mWakeEventFd寫(xiě)數(shù)據(jù)和讀數(shù)據(jù)實(shí)現(xiàn)的。

1.6 Looper.cpp#rebuildEpollLocked

    void Looper::rebuildEpollLocked() {
        // Close old epoll instance if we have one.
        // mEpollFd存在,則重新設(shè)置
        if (mEpollFd >= 0) {
    #if DEBUG_CALLBACKS
            ALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this);
    #endif
            mEpollFd.reset();
        }

        // Allocate the new epoll instance and register the wake pipe.
        //調(diào)用epoll_create1方法重新創(chuàng)建,這個(gè)方法會(huì)返回一個(gè)fd并賦值給mEpollFd
        mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
        LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));

        struct epoll_event eventItem;
        memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
        eventItem.events = EPOLLIN;
        eventItem.data.fd = mWakeEventFd.get();
        //通過(guò)epoll_ctl方法來(lái)添加一個(gè)event
        int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);
        LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",
                            strerror(errno));

        //若mRequests中存在,則依次調(diào)用epoll_ctl方法添加event
        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.get(), 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));
            }
        }
    }

在介紹上面方法之前,先來(lái)介紹下fd管道,管道,eventfd,再來(lái)介紹epoll機(jī)制。

管道,F(xiàn)IFO,fd管道,eventfd
這節(jié)不是介紹handler機(jī)制嗎?怎么涉及到了管道這些內(nèi)容,主要原因是咱們的handler機(jī)制已經(jīng)不是單單解決線程之間通信的問(wèn)題,還解決進(jìn)程之間的通信。

管道:是半雙工的(就是數(shù)據(jù)只能在一個(gè)方向上流動(dòng)),只能有公共祖先的兩個(gè)進(jìn)程之間使用
FIFO:也是一種管道,它沒(méi)有管道的只能在公共祖先的兩個(gè)進(jìn)程之間使用的限制
fd管道:又稱UNIX 域套接字,它是全雙工的(一端既可以是寫(xiě)端也可以是讀端),并且對(duì)進(jìn)程沒(méi)有限制。vsync機(jī)制就使用的是fd管道
eventfd:上面已經(jīng)介紹了,它的優(yōu)點(diǎn)高效/少占用資源,缺點(diǎn):傳播的數(shù)據(jù)只能是int。

這四者都可以實(shí)現(xiàn)線程之間/進(jìn)程之間的通信,并且它們的read,write的目標(biāo)都是fd(文件描述符)。它們實(shí)現(xiàn)通信的方式如上面eventfd的偽代碼一樣,在創(chuàng)建的時(shí)候需要設(shè)置為阻塞模式,在阻塞模式下read,write函數(shù)都是阻塞的,因此尤其read函數(shù)的調(diào)用就需要放在單獨(dú)的線程中。那如果創(chuàng)建了很多個(gè)管道來(lái)實(shí)現(xiàn)線程之間通信,那豈不是要?jiǎng)?chuàng)建很多的讀線程,隨著管道的數(shù)量多起來(lái),讀線程也增加,這種方式肯定不是一個(gè)好的解決方案,解決這個(gè)問(wèn)題的一個(gè)技術(shù)是IO多路復(fù)用。

epoll
IO多路復(fù)用:我的理解是比如原先創(chuàng)建一個(gè)阻塞型的管道,那就需要?jiǎng)?chuàng)建一個(gè)讀線程專門(mén)的來(lái)監(jiān)聽(tīng)管道的讀端是否有數(shù)據(jù),那創(chuàng)建n多個(gè)阻塞型管道,那就需要?jiǎng)?chuàng)建n多個(gè)讀線程;那IO多路復(fù)用就是只創(chuàng)建一個(gè)讀線程,來(lái)監(jiān)聽(tīng)n多個(gè)管道讀端的數(shù)據(jù)。

IO多路復(fù)用的優(yōu)勢(shì)是不是很明顯,它有多種實(shí)現(xiàn):select機(jī)制,poll機(jī)制,epoll機(jī)制。epoll機(jī)制是最高效,優(yōu)點(diǎn)最多的一個(gè)機(jī)制,用一段偽代碼來(lái)介紹下它的使用:

    //1. 調(diào)用epoll_create1方法先創(chuàng)建,該方法返回一個(gè)fd
    epollFd = epoll_create1(EPOLL_CLOEXEC);

    //2. 調(diào)用epoll_ctl方法添加一個(gè)event,epoll_ctl方法的第一個(gè)參數(shù)就是epollFd。這樣event就和epollFd綁定在了一起,epollFd就可以監(jiān)聽(tīng)event上的數(shù)據(jù)了
    struct epoll_event eventItem;
    //event類型
    eventItem.events = EPOLLIN;
    //event對(duì)應(yīng)的fd
    eventItem.data.fd = pipeFd;
    int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);

    //3. 調(diào)用epoll_wait方法開(kāi)始監(jiān)聽(tīng)所有event的數(shù)據(jù)。在沒(méi)有監(jiān)聽(tīng)到數(shù)據(jù)的情況下,會(huì)進(jìn)入阻塞狀態(tài),會(huì)釋放cpu等資源。參數(shù)timeoutMillis代表等待時(shí)間,== 0代表不等待立馬返回,== -1 則代表等待,直到等待數(shù)據(jù)為止,> 0則代表需要等待的時(shí)間。
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

總結(jié)下epoll的用法:

  1. 初始化,調(diào)用epoll_create1方法創(chuàng)建并返回一個(gè)fd
  2. 添加event事件,調(diào)用epoll_ctl方法
  3. 等待數(shù)據(jù)到來(lái),調(diào)用epoll_wait,它的參數(shù)timeoutMillis代表等待時(shí)間,== 0代表不等待立馬返回,== -1 則代表等待,直到等待數(shù)據(jù)為止,> 0則代表需要等待的時(shí)間。在沒(méi)有數(shù)據(jù)到來(lái)的時(shí)候,會(huì)進(jìn)入阻塞狀態(tài),會(huì)釋放cpu等資源

rebuildEpollLocked
那就來(lái)看下rebuildEpollLocked方法所做的事情:

  1. 調(diào)用epoll_create1方法創(chuàng)建并返回fd賦值給mEpollFd
  2. 把mWakeEventFd關(guān)聯(lián)的epoll_event,調(diào)用epoll_ctl方法添加該事件,這樣mEpollFd就和mWakeEventFd產(chǎn)生了關(guān)聯(lián),就可以監(jiān)聽(tīng)它的數(shù)據(jù)了
  3. 把mRequests中的請(qǐng)求也通過(guò)epoll_ctl方法添加這些事件。mRequests包含了各種需要監(jiān)聽(tīng)的fd,比如:vsync機(jī)制就是通過(guò)fd管道實(shí)現(xiàn)的,其中一個(gè)fd在surfaceflinger進(jìn)程,另外一個(gè)對(duì)應(yīng)的fd位于app進(jìn)程,位于app進(jìn)程的fd會(huì)通過(guò)Looper.cpp#addFd方法把自己加入mRequests,這樣就可以通過(guò)epoll來(lái)監(jiān)聽(tīng)是否有數(shù)據(jù)到來(lái)

1.7 小結(jié)

到此Looper.prepare()方法的流程就分析完了,用一張時(shí)序圖來(lái)看下整個(gè)調(diào)用流程


handler-looper-prepare

2.Looper.loop()

再來(lái)分析下Looper.loop()這個(gè)方法的流程

    public static void loop() {
        //獲取線程綁定的Looper
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        if (me.mInLoop) {
            Slog.w(TAG, "Loop again would have the queued messages be executed"
                    + " before this one completed.");
        }

        me.mInLoop = true;

        省略代碼......

        //啟動(dòng)一個(gè)死循環(huán)
        for (;;) {
            [2.1]
            if (!loopOnce(me, ident, thresholdOverride)) {
                return;
            }
        }
    }

    public static @Nullable Looper myLooper() {
        //從ThreadLocal中獲取Looper
        return sThreadLocal.get();
    }

2.1 Looper.loopOnce

    private static boolean loopOnce(final Looper me,
            final long ident, final int thresholdOverride) {
        //從MessageQueue中取Message,若沒(méi)有可執(zhí)行的Message 則block
        [2.2]
        Message msg = me.mQueue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return false;
        }

        省略代碼......

        try {
            msg.target.dispatchMessage(msg);
            if (observer != null) {
                observer.messageDispatched(token, msg);
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } catch (Exception exception) {
            if (observer != null) {
                observer.dispatchingThrewException(token, msg, exception);
            }
            throw exception;
        } finally {
            ThreadLocalWorkSource.restore(origWorkSource);
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        
        省略代碼......

        msg.recycleUnchecked();

        return true;
    }

me.mQueue.next()會(huì)進(jìn)入MessageQueue.next()方法獲取Message,進(jìn)入該方法

2.2 MessageQueue.next

    Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;

        //同樣起一個(gè)死循環(huán)
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            //進(jìn)入jni方法, 這時(shí)候nextPollTimeoutMillis的值是-1
            [2.3]
            nativePollOnce(ptr, nextPollTimeoutMillis);

            省略獲取Message的代碼...... (下一節(jié)會(huì)重點(diǎn)介紹)
        }
    }

nativePollOnce方法最終會(huì)調(diào)jni的android_os_MessageQueue_nativePollOnce方法

2.3 android_os_MessageQueue.cpp#android_os_MessageQueue_nativePollOnce

    //obj java層的MessageQueue, ptr NativeMessageQueue指針,timeoutMillis值為-1
    static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
            jlong ptr, jint timeoutMillis) {
        //把ptr轉(zhuǎn)換為NativeMessageQueue指針
        NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
        [2.4]
        nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
    }

2.4 android_os_MessageQueue.cpp#nativeMessageQueue#pollOnce

    void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
        mPollEnv = env;
        mPollObj = pollObj;
        //調(diào)用pollonce方法,timeoutMillis值為-1
        [2.5]
        mLooper->pollOnce(timeoutMillis);
        mPollObj = NULL;
        mPollEnv = NULL;

        if (mExceptionObj) {
            env->Throw(mExceptionObj);
            env->DeleteLocalRef(mExceptionObj);
            mExceptionObj = NULL;
        }
    }

2.5 Looper.cpp#pollOnce

    //下面方法在system/core/libutils/include/utils/Looper.h
    int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
    inline int pollOnce(int timeoutMillis) {
        //調(diào)用了pollOnce的重載方法,outFd,outEvents,outData都為nullptr
        return pollOnce(timeoutMillis, nullptr, nullptr, nullptr);
    }


    //下面方法在system/core/libutils/Looper.cpp
    //timeoutMillis為-1
    int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
        int result = 0;
        //同樣死循環(huán)
        for (;;) {
            //若有沒(méi)處理的response,則處理,并返回
            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 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 != nullptr) *outFd = fd;
                    if (outEvents != nullptr) *outEvents = events;
                    if (outData != nullptr) *outData = data;
                    return ident;
                }
            }

            //若result不為0,則返回,剛開(kāi)始進(jìn)入這方法,result是0
            if (result != 0) {
    #if DEBUG_POLL_AND_WAKE
                ALOGD("%p ~ pollOnce - returning result %d", this, result);
    #endif
                if (outFd != nullptr) *outFd = 0;
                if (outEvents != nullptr) *outEvents = 0;
                if (outData != nullptr) *outData = nullptr;
                return result;
            }
            //進(jìn)入pollInner方法
            [2.6]
            result = pollInner(timeoutMillis);
        }
    }

2.6 Looper.cpp#pollInner

    //timeoutMillis值為-1
    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.
        //因?yàn)閠imeoutMillis當(dāng)前值為-1 并且 mNextMessageUptime在Looper構(gòu)造方法初始化的時(shí)候,它的值為L(zhǎng)LONG_MAX,因此不會(huì)進(jìn)入下面調(diào)整timeout的邏輯
        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.
        mPolling = true;

        //定義eventItems數(shù)組它主要接受傳遞過(guò)來(lái)的event, 調(diào)用epoll_wait方法開(kāi)始等待event,因?yàn)閠imeoutMillis當(dāng)前的值為-1,因此會(huì)阻塞等待events,并且釋放cpu等資源
        struct epoll_event eventItems[EPOLL_MAX_EVENTS];
        int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

        省略掉處理wakefd和mRequest的代碼......(后面章節(jié)會(huì)詳細(xì)講解)

        return result;
    }

該方法會(huì)調(diào)用epoll_wait方法等待events,因?yàn)閠imeoutMillis當(dāng)前的值為-1,因此會(huì)使當(dāng)前線程進(jìn)入阻塞狀態(tài),并釋放cpu等資源

2.7 小結(jié)

到此 Looper.loop()方法的流程就分析完了,用一張時(shí)序圖總結(jié)下:


handler-looper-loop

總結(jié)

讓線程變?yōu)椤坝绖?dòng)機(jī)”可以分為兩個(gè)步驟:

  1. 準(zhǔn)備階段,Looper.prepare()方法其實(shí)做的都是準(zhǔn)備工作:

    • 初始化Looper對(duì)象,并且把他放入ThreadLocal中,放入ThreadLocal的主要作用就是:為了能在當(dāng)前線程的任何代碼處非常方便的獲取到當(dāng)前線程”綁定“的Looper。
    • 初始化MessageQueue對(duì)象,它與上面初始化的Looper對(duì)象是一對(duì)一關(guān)系
    • 初始化native層的Looper對(duì)象,調(diào)用eventfd方法創(chuàng)建fd;調(diào)用epoll_create方法創(chuàng)建epollfd,并且依次調(diào)用epoll_ctl把mWakeEventfd以及mRequest封裝成event并添加,這樣就可以通過(guò)epoll機(jī)制來(lái)監(jiān)聽(tīng)它所添加的event上面的事件是否發(fā)生了
  2. 開(kāi)始工作階段,在線程中調(diào)用Looper.loop()方法后就開(kāi)始工作了:

    • Looper的loop方法會(huì)啟動(dòng)一個(gè)死循環(huán),這樣一個(gè)線程就真正的變成“永動(dòng)機(jī)”了
    • 啟動(dòng)死循環(huán)后,會(huì)調(diào)用MessageQueue的next方法從中獲取Message,next方法調(diào)用了nativePollOnce方法,最終會(huì)調(diào)用到Looper.cpp的pollInner方法,由于第一步準(zhǔn)備階段,epoll,eventfd相關(guān)的準(zhǔn)備工作都已經(jīng)準(zhǔn)備好了,pollInner方法中會(huì)調(diào)用epoll_wait方法等待事件到來(lái),因?yàn)檫@時(shí)候的等待時(shí)間(timeoutMillis)為-1,會(huì)一直等待,直到有事件發(fā)生為止,進(jìn)而導(dǎo)致當(dāng)前的線程進(jìn)入阻塞狀態(tài),并釋放cpu等資源

因?yàn)閔andler使用了epoll機(jī)制,handler既可以實(shí)現(xiàn)線程之間通信,也可以實(shí)現(xiàn)進(jìn)程之間通信。

好了,到此線程已經(jīng)做好了一切準(zhǔn)備,就等待著“各種事件“的到來(lái)了。

思考

MessageQueue的next方法獲取消息時(shí)候 等待/喚醒 實(shí)現(xiàn)方案為啥沒(méi)用 wait/notify 來(lái)實(shí)現(xiàn)?
上面源碼分析提到MessageQueue的next方法最終是因?yàn)閑poll_wait方法,導(dǎo)致線程進(jìn)入 等待阻塞狀態(tài)的,那為啥沒(méi)有使用wait/notify來(lái)實(shí)現(xiàn)呢?大家其實(shí)可以找很早以前的android代碼,那時(shí)候確實(shí)是用wait/notify來(lái)實(shí)現(xiàn) 等待/喚醒機(jī)制。

我認(rèn)為的主要原因是:用epoll機(jī)制實(shí)現(xiàn)的 等待/喚醒機(jī)制,主要是它有如下優(yōu)點(diǎn):

  • 功能強(qiáng)大:不僅實(shí)現(xiàn)線程之間通信,還實(shí)現(xiàn)進(jìn)程之間通信的功能,vsync機(jī)制就是利用fd管道實(shí)現(xiàn)進(jìn)程通信,epoll只需要監(jiān)聽(tīng)fd管道的一端fd上的數(shù)據(jù)狀態(tài)即可,surfaceflinger進(jìn)程往對(duì)端fd上寫(xiě)數(shù)據(jù),epoll在當(dāng)前的線程中就可以監(jiān)聽(tīng)到surfaceflinger發(fā)過(guò)來(lái)的數(shù)據(jù)。
  • 高性能: epoll機(jī)制可以監(jiān)聽(tīng)n多個(gè)fd,并且不會(huì)隨著fd的增加而性能下降
  • 擴(kuò)展性好: 只需要調(diào)用Looper.cpp的addFd方法就可以在當(dāng)前線程監(jiān)聽(tīng)fd上的數(shù)據(jù)
  • 既可以為java層提供服務(wù),也可以為native層提供服務(wù):native層的Looper.cpp類其實(shí)也提供了和上層MessageQueue相關(guān)的功能

假如使用wait/notify實(shí)現(xiàn)進(jìn)程之間通信就困難了,并且即使實(shí)現(xiàn)了還可能會(huì)涉及到線程之間的切換,性能方面肯定大打折扣。

為啥要用eventfd機(jī)制?
在把Message放入MessageQueue的時(shí)候,這時(shí)候只是需要給阻塞的MessageQueue發(fā)一個(gè)有多簡(jiǎn)單就能多簡(jiǎn)單的通知或者信號(hào)就行,告訴它有消息到達(dá)因。為沒(méi)有用 wait/notify 來(lái)實(shí)現(xiàn) 等待/喚醒,所以就需要用管道這類技術(shù)來(lái)實(shí)現(xiàn),但是用管道做這種事情又大才小用了,evenfd是最合適,它占用的內(nèi)存非常小并且只使用一個(gè)fd,并且它就是發(fā)送一個(gè)int類型的值就可以,因此使用了eventfd來(lái)實(shí)現(xiàn):把Message放入MessageQueue的時(shí)候,通知阻塞的MessageQueue有消息到來(lái)了這樣的功能。

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

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

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