消息处理机制之消息循环

Android引用程序的消息队列创建完成之后,就会调用Looer类的loop()方法,进入到一个消息循环中。

在这里插入图片描述
按照流程图分析android源码的调用过程

  1. Looper:loop()
    frameworks/base/core/java/android/os/Looper.java
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(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ...
        }
    }

首先获取当前线程的消息队列,接着会调用在无限for循环中获取消息队列中的消息。如果消息队列中有消息存在,获取的到msg就不为null,否则就会在MessageQueue的next()方法中进入睡眠等待状态,直到有新的新的消息要处理。如果获取到的消息为null,表明退出了消息队列。

frameworks/base/core/java/android/os/MessageQueue.java

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;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    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 {
                        // 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;
                }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

在函数调用nativePollOnce时线程可能进入休眠状态,这取决于nextPollTimeoutMillis,如果其值等于为0,表示即使消息队列中没有消息也不进入休眠状态,如果其值大于0,表示需要休眠nextPollTimeoutMillis的时间,如果为-1表示需要消息队列中没有消息需要处理,当前线程需要无期限的等待下去,直到被其它的线程唤醒为止。

frameworks/base/core/jni/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);
}
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;
    }
}

MessageQueue的类成员函数nativePollOnce是一个JNI方法,它在android_os_MessageQueue中对应的方法为android_os_MessageQueue_nativePollOnce。参数ptr指向的是MessageQueue.java中的成员变量mPtr,接着将其转换为一个NativeMessageQueue对象,然后调用NativeMessageQueue中的pollOnce方法,其会调用Looper.cpp的pollOnce方法。

system/core/libutils/Looper.cpp

int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    for (;;) {
       
       ...

        if (result != 0) {

            if (outFd != nullptr) *outFd = 0;
            if (outEvents != nullptr) *outEvents = 0;
            if (outData != nullptr) *outData = nullptr;
            return result;
        }

        result = pollInner(timeoutMillis);
    }
}

pollOnce方法中不断检测pollInner方法的返回值,如果不为0,就会返回。

int Looper::pollInner(int timeoutMillis) {
 
    ...
    
    // Poll.
    int result = POLL_WAKE;

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

    ...

    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeEventFd.get()) {
            if (epollEvents & EPOLLIN) {
                awoken();
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
            }
        } else {
            ...
        }
    }
    ...
    
    return result;
}

当前线程在C++层创建了一个epoll实例,并将它的文件描述符保存在C++层Looper类的变量mEpollFd中,同时还将一个管道的读端文件描述符注册到它的里面以便监听这个管道的IO写事件。

调用函数epoll_wait来监听注册在epoll实例中的IO读写事件,如果这些文件没有发生IO读写事件,当前线程就会在epoll_wait中进入睡眠等待,等待的时间有timeoutMillis确定。

从函数epoll_wait返回后就会进入for循环,if语句会检测发生IO读写事件的文件描述符是否与当前线程所关联的一个管道端的文件描述符mWakeEventFd.get()。如果
是,并且发生的IO读写事件的类型为EPOLLIN,那么这时候说明其他线程向与当前线程相关联的管道写入了一个新的数据。接下来就会调用awoken()方法。

void Looper::awoken() {
    uint64_t counter;
    TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
}

在awoken中将调用函数read将当前线程相关联的数据读出来。

在这里可以看出线程根本不关心写入到管道中的内容,只是简单的将其读取出来,以清除管道中的旧数据。这样就会在下一次消息循环时,监听IO写事件如果没有新的消息需要处理就会进入睡眠等待状态就会进入睡眠等待状态,直到有其他线程向它的消息队列中发送一个新消息为止。

参考资料
《Android系统源代码情景分析》罗升阳 著

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章