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

Fxymine4ever發表於2019-03-19

訊息機制常常是Android開發中比較核心的一部分,作為Android開發者這部分是一定要掌握的。本文通過結合Native層的部分程式碼來分析Android framework層訊息機制。

前言

由於Android規定在子執行緒中無法更新UI,而網路請求一般又只能在子執行緒中進行。這時,當我們請求到網路之後必定要將資料展示在UI上,所以說Android為我們提供了好多好多的方法來更新UI。

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

要了解原理,才能更好地使用Handler,所以說本文將帶你走入Android訊息機制的世界裡。

基本使用

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處理。

原始碼分析

說起看原始碼,我大概的步驟是先看構造器,然後再根據我們日常使用的流程進行分析,明白各個類之間的關係。如果有更有效方法,可以傳授一下。訊息機制,無非就是HandlerLooperMessageQueue之間的關係。

關係

  • Handler:訊息處理和接收者
  • Looper:訊息排程者
  • MessageQueue:訊息儲存佇列

建構函式

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

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則會丟擲異常。

Looper建構函式
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
複製程式碼

Looper的建構函式中對MessageQueue進行了初始化,並且繫結了當前執行緒。但是值得一提的是這個構造器被private了,意思是不能顯式地例項化,所以說我們要尋找在哪裡初始化了Looper

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學習之路還很漫長。

相關文章