由外到內——剖析Android消息機制

消息機制經常是Android開發中比較核心的一部分,做爲Android開發者這部分是必定要掌握的。本文經過結合Native層的部分代碼來分析Android framework層消息機制。java

前言

因爲Android規定在子線程中沒法更新UI,而網絡請求通常又只能在子線程中進行。這時,當咱們請求到網絡以後一定要將數據展現在UI上,因此說Android爲咱們提供了好多好多的方法來更新UI。android

Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
Handler
複製代碼

要了解原理,才能更好地使用Handler,因此說本文將帶你走入Android消息機制的世界裏。c++

基本使用

public class MainActivity extends AppCompatActivity {
    private static final int CODE = 1;
    private TextView tv;
    private Handler mainHandler;



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv = findViewById(R.id.tv);
        mainHandler = new Handler(getMainLooper()){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                if(msg.what == CODE){
                    tv.setText((String)msg.obj);
                }
            }
        };

        new Thread(()->{
            try {
                Thread.sleep(3000);//模擬網絡耗時
                Message message = Message.obtain();
                message.what = CODE;
                message.obj = "子線程用Handler更新UI";
                mainHandler.sendMessage(message);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mainHandler.removeCallbacksAndMessages(null);
    }
}
複製代碼

代碼很簡單,無非就是在子線程中將Message發送到主線程中,再利用Handler處理。bash

源碼分析

提及看源碼,我大概的步驟是先看構造器,而後再根據咱們平常使用的流程進行分析,明白各個類之間的關係。若是有更有效方法,能夠傳授一下。消息機制,無非就是HandlerLooperMessageQueue之間的關係。網絡

關係

  • Handler:消息處理和接收者
  • Looper:消息調度者
  • MessageQueue:消息存儲隊列

構造函數

首先咱們分析三個重要類的構造方法less

Handler構造函數

構造函數一共有6個,但最重要的是下面這一個,其他構造器均是重載的下面這個構造器。異步

public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
複製代碼

咱們能夠看到,首先在這裏進行判斷,若是找到了潛在的內存泄露則會打印一個Log。以後就是得到Handler所在線程的LooperMessageQueue。若是未找到Looper,即沒有執行Looper.prepare則會拋出異常。async

Looper構造函數
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
複製代碼

Looper的構造函數中對MessageQueue進行了初始化,而且綁定了當前線程。可是值得一提的是這個構造器被private了,意思是不能顯式地實例化,因此說咱們要尋找在哪裏初始化了Looperide

Looper.prepare

通過一番尋找,果真在Looper.prepare方法中找到了Looper實例化的地方。函數

public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
複製代碼

這裏進行了3件事情

  1. 判斷ThreadLocal裏是否已經存在Looper,若存在了則拋出異常。TheadLocal在每一個線程中只存在一個,通常用來保存每一個線程獨有的東西。
  2. 初始化Looper。在Looper.prepare中,quitAllowed默認爲true。這個quitAllowed會傳入MessageQueue的構造器裏,做用是判斷是否Message能夠退出,這裏以後會講。
  3. 將剛剛初始化的Looper保存到當前線程的ThreadLocal中。
MessageQueue構造函數
MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }
複製代碼

MessageQueue的構造函數中,設置了是否容許Message退出,而後調用nativeInit方法初始化native層的nativeMessageQueue,以後再將生成的nativeMessageQueue,經過reinterpret_cast方法將指針轉化爲long值返回給Framework層保存。

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);
}
複製代碼

消息入隊

Handler.sendMessage

Handler發送消息有不少種:

  1. sendMessage
  2. sendMessageDelayed
  3. MessageAtTime
  4. sendMessageAtFrontOfQueue
  5. post(篇幅有限,這個方法邏輯差很少。)
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);
    }
複製代碼

sendMessagesendMessageDelayed最後都會調用sendMessageAtTime,惟一不一樣的是發送延時消息時,參數須要傳入(當前時間+延時時間)

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    public final boolean sendMessageAtFrontOfQueue(Message msg) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, 0);
    }
複製代碼

sendMessageAtTimesendMessageAtFrontOfQueue均會調用enqueueMessage,不一樣的是sendMessageAtTime傳入的時間是uptimeMillis,而sendMessageAtFrontOfQueue傳入的是0。顧名思義,後者是想把把消息插在消息隊列的比隊頭。

因此說咱們大膽猜想一下,隊列會根據uptimeMillis值的不一樣來插入隊列。咱們接着往下看。

Handler.enqueueMessage
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
複製代碼

邏輯很簡單,這個方法將messagetarget設置爲當前的Handler,而後設置是否異步發送該消息,最後調用MessageQueue.enqueueMessage

MessageQueue.enqueueMessage
boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {//message沒有target咋會知道發送到哪兒?
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {//使用過的message確定不能再用吧?
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {//若是Message正在退出,會打印Log而且回收Message
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();//這個方法會把message回收到Message池
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            //下面是對message進行處理
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue. Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
複製代碼

能夠看出,MessageQueue使用Message.next保存下一個Message,從而按照時間將全部的Message排序,從而達到入隊的效果。同時,此時若是須要喚醒,則調用nativeWake方法來喚醒以前等待的線程。

static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->wake();
}
複製代碼

這樣,咱們就探索完Message是如何進入消息隊列的邏輯。

消息分發和處理

在消息機制中,Looper主要做用是用來負責輪訓消息、分發消息的。接下來咱們分析一個消息是如何分發的。首先咱們看Looper.loop方法

Looper.loop
public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        //...省略一波代碼

        for (;;) {
            Message msg = queue.next(); //取出Message
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            //...省略一波代碼

            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            //省略一波代碼target

            msg.recycleUnchecked();
        }
    }
複製代碼

能夠看到,經過msg.target將消息分發下去,最後回收了msg。這裏的msg.target就是在Handler.enqueueMessage設置的Handler。因此說這裏調用的是Handler.dispatchMessage方法

Handler.dispatchMessage
public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
複製代碼

若是該消息的callback不爲空,則容許該消息的callback對象的run方法。不然,若是當前Handler.mCallback屬性不爲空,則mCallback處理該消息,若是msg.callbackHandler.mCallback都爲空,則直接利用Handler.handleMessage內部處理該消息,該方法就是handler初始化時,本身寫的方法。

借用一下僞代碼:

public void dispatchMessage(Message msg) {
        if (該消息的callback屬性不爲空) {
            運行該消息的callback對象的run()方法;
        } else {
            if (當前Handler對象的mCallback屬性不爲空) {
                if (mCallback對象成功處理了消息) {
                    return;
                }
            }
            Handler內部處理該消息;
        }
    }
複製代碼

另外,在Looper.loop方法中,消息從消息隊列中被取出。彷佛咱們還沒分析消息如何取出來的吧?那咱們立刻分析一下姍姍來遲的MessageQueue.next

MessageQueue.next
Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            //..省略一大波代碼

            nativePollOnce(ptr, nextPollTimeoutMillis);//使Native的Looper阻塞

            synchronized (this) {
                // Try to retrieve the next message. Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                
                //若是msg不爲null,target(目標Handler)不爲空
                if (msg != null && msg.target == null) {
                    do {//取出第一個異步消息
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {//若是還沒到時間
                        // Next message is not ready. Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {//到時間就取出message
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;//返回消息
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;//沒有更多消息則阻塞本身
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }
                //..省略一大波代碼
            }

            //..省略一大波代碼
        }
    }
複製代碼

根據我寫的註釋咱們能夠知道,在這個方法裏會有一個死循環不斷輪訓消息,若是沒有消息則該循環會被阻塞,若是有消息,該消息會被移出單鏈表,而後返回該消息。

消息機制流程圖

image

Message回收複用

在註釋中,咱們發現Google推薦用Message.obtain方法來得到一個Message而不是new一個Message。因此咱們這裏探索一下是爲何呢?

Message.obtain
public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
複製代碼

顯而易見,這裏將運用了相似於對象池的東西,不過實現方法確實單鏈表。當sPool不爲空,則說明該單鏈表上還有回收的Message能夠複用。不然則建立一個新的Message。咱們在以前的源碼裏看見了Message.recycle方法,這個方法用於在回收Message前進行一系列檢查,真實調用的是Message.recycleUnchecked

Message.recycleUnchecked
void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }
複製代碼

在這裏清空Message的全部屬性,而且將其放入對象池中。

這就是Message如何複用的過程。

Native層淺析

native層的邏輯與framework層大體相同,不過實現方法略有不一樣。值得一提的是,native層中NativeMessageQueue已經被弱化掉了,大部分邏輯都放在NativeLooper中處理。

image

Native層初始化

在上面,咱們提到了MessageQueue的構造函數中同時Native層也要初始化MessageQueue,前面也提到,這裏的MessageQueue指針將會被轉化成long值保存在framework的MessageQueue中。

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);
}
複製代碼

在MessageQueue中一樣也和Framework層同樣,初始化了一個Native層的Looper,同時把Looper保存在當前線程中。

NativeMessageQueue::NativeMessageQueue() :
        mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false);
        Looper::setForThread(mLooper);
    }
}
複製代碼

Native層的Looper構造函數以下:

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();
}
複製代碼

這裏建立了mWakeEventFd,與以前5.0利用管道的mWakeWritePipeFdmWakeReadPipeFd實現讀取/寫入不同,eventfd是等待/響應。同時這裏有一個很重要的方法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)); // zero out unused members of data field union
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mWakeEventFd;
    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));

    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));
        }
    }
}
複製代碼

rebuildEpollLocked方法中,經過epoll_create建立了一個epoll專用的文件描述符,參數中的EPOLL_SIZE_HINTmEpollFd最大可監聽的文件描述符數。以後調用epoll_ctl方法對mWakeEventFd的Epoll事件進行監聽,mWakeEventFd若是有事件可讀,就會喚醒當前正在等待的線程。

Epoll是什麼?

剛剛提到了Epoll,可能沒有了解的人根本不知道Epoll是什麼東西。當framework層Looper.loop的時候,會用MessageQueue.next來取出消息。而next方法中是一個暴力死循環,Android固然沒這麼傻,不可能一直循環,因此說在循環裏又調用了nativePollOnce(ptr, nextPollTimeoutMillis)方法,這裏就是消息隊列阻塞的關鍵,也是Epoll的主要做用。

回到正題,傳統的阻塞型I/O,一個線程只能處理一個一個的IO流,就像5.0版本同樣利用mWakeWritePipeFdmWakeReadPipeFd實現讀寫。而若是想要一個線程處理多個流,就得采用非阻塞、輪訓的I/O,這樣就出現了select(無差異輪詢)epoll(事件輪詢)這兩種方法。而Epoll(相似於Event poll,即事件輪詢)是基於事件的,不會無差異輪詢,性能會有提升。

因此說咱們看看剛纔的rebuildEpollLocked方法中幹了什麼事情:

mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mWakeEventFd;
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
複製代碼
  1. 建立mEpollFd代理
  2. epoll_event中設置mWakeEventFd
  3. 而後監聽mWakeEventFd中的Epoll事件

這就是EPOLLIN事件所作的事情。

因此說,native層主要負責的是消息的調度,什麼時候阻塞線程、什麼時候喚醒線程、避免不停地空循環,避免性能消耗過大。

Native層消息發送

Handler發送消息的時候,會調用MessageQueue.enqueueMessage方法。在這個方法裏,若是線程阻塞,又會調用nativeWake來喚醒線程。咱們接下來看看nativeWake方法的實現

static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->wake();
}
複製代碼

經過ptr(就是framework層保存的long值)來拿到NativeMessageQueue,其實是調用的wake方法

void NativeMessageQueue::wake() {
    mLooper->wake();
}
複製代碼

最後調用了Looper的wake方法

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裏面寫一個無用的事件,直到framework層MessageQueue.next被喚醒。

native層消息接收

前面咱們提到在MessageQueue.next方法中,會調用一個native方法nativePollOnce(ptr, nextPollTimeoutMillis)來阻塞線程

Message next() {
        //..省略一大波代碼
        int nextPollTimeoutMillis = 0;
        for (;;) {
            //..省略一大波代碼

            nativePollOnce(ptr, nextPollTimeoutMillis);//使Native的Looper阻塞

            synchronized (this) {
               //..省略一大波代碼
            }

            //..省略一大波代碼
        }
    }
複製代碼

咱們看看它的具體實現:

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);
}
複製代碼

一樣是調用的NativeMessageQueuepollOnce方法

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;
    }
}
複製代碼

和以前的wake同樣,這裏一樣最終調用了LooperpollOnce方法,因此說,native層的MessageQueue實際上已經被弱化了許多,大多數操做都在Looper中進行:

int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    for (;;) {
        while (mResponseIndex < mResponses.size()) {
            //...省略一波代碼
        }

        //...省略一波代碼

        result = pollInner(timeoutMillis);
    }
}
複製代碼

pollOnce中又是一個死循環,就相似於MessageQueue.next方法同樣,最終會調用pollInner(timeoutMillis)方法,這裏就是最終的調用。

int Looper::pollInner(int timeoutMillis) {
        //...
    int result = POLL_WAKE;
    mResponses.clear();
    mResponseIndex = 0;

    // We are about to idle.
    mPolling = true;

    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

    // No longer idling.
    mPolling = false;

    // Acquire lock.
    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();//經過awoken喚醒線程
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
            }
        } else {
            //...
        }
    }


    // Release lock.
    mLock.unlock();
        //...
    return result;
}
複製代碼

這裏能夠看到,利用epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis)方法獲取到事件到個數。若是個數爲0,就繼續阻塞。若是不爲0,則會遍歷每個事件,若是有mWakeEventFd而且是EPOLLIN事件,就會經過awoken方法真正地喚醒線程。

framework與native關係圖

image

最後

最後附上native層代碼:
NativeMessageQueue
Looper 歡迎你們來和我交流。Android學習之路還很漫長。

相關文章
相關標籤/搜索