Handler中Looper死循环为什么不会导致应用卡死?(未完成,待继续更新)

190 阅读3分钟

点击app图标,会进入先创建Application进程然后再启动application的ActivityThread的main方法入口

public static void main(String[] args) {
        ...

        Looper.prepareMainLooper();
        ...
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
        ...
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

从上面的代码可以看出,最主要是创建Looper并启动循环,我们来看下Looper.java的loop()函数

public static void loop() {
        final Looper me = myLooper();
        ...

        for (;;) {
            Message msg = queue.next(); // might block
            ...
            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();
        }
    }

我们可以看到这里的for循环是个死循环,但为什么不会卡死程序了。

从MessageQueue取Message时调用了queue.next()函数,

在MessageQueue.java的next()函数的代码如下

@UnsupportedAppUsage
    Message next() {
         ...
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

          ...
        }
    }

我们看到了最重要的函数,nativePollOnce,这是一个native的函数,也就是一个用c++写的函数。

MessageQueue.java里有三个非常重要的native函数

private native static long nativeInit();//MessageQueue初始化时调用,返回的long其实是MessageQueue的操作句柄
private native void nativePollOnce(long ptr, int timeoutMillis);//取消息时用 
private native static void nativeWake(long ptr);//唤醒消息队列时使用

我们重要来看下nativePollOnce()方法

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

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);
}

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

我们在AndroidRuntime.cpp里能找到MessageQueue的JNI注册地方

image.png

点击进入到android_os_MessageQueue.cpp文件里,可以看到MessageQueue.java里的6个native方法

image.png

image.png

我们来看下nativePollOnce()方法

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

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;
    }
}

void NativeMessageQueue::wake() {
    mLooper->wake();
}

可认看到NativeMessageQueue::pollOnce方法调用到了Looper.pollOnce,而此时的Looper是

system/core/libutils/Looper.cpp
Looper::Looper(bool allowNonCallbacks) :
        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
        mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
        mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
    // 创建事件句柄mWakeEventFd,用于唤醒
    mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);

    rebuildEpollLocked();
}

void Looper::rebuildEpollLocked() {
    // 创建epoll句柄
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);

    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;
    // 把mWakeEventFd加入epoll监听,监听写入(EPOLLIN)事件
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
}

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

int Looper::pollInner(int timeoutMillis) {
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    // 等待监听事件
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    ... ...
    return result;
}

void Looper::wake() {
    uint64_t inc = 1;
    // 向mWakeEventFd写入数据(写入什么数据并不重要),用于唤醒消息队列
    // 调用这个方法后epoll_wait函数会返回,沿着调用链一路向上pollInner->pollOnce->pollOnce->android_os_MessageQueue_nativePollOnce->nativePollOnce->next
    // 这个时候Looper.loop()调用的next()会返回,循环体继续执行
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
}

这里就是Android系统的handler消息机制的循环核心实现,使用了linux的epoll事件处理机制。当消息队列为空时,常规的循环实现的调用sleep/usleep释放cpu,当时间到了之后,会获取cpu资源进行一次消息检查;Handler消息循环采用Linux 内核的epoll机制,当消息列表为空时,会挂起释放cpu资源 ,只有当消息列表有消息时,才会重新获取cpu资源,进行消息处理。

Android的消息机制的生产者,会把消息放入消息列表的同时,通知唤醒消息处理者(Looper)

下面我们来说说为什么epoll如此高效。