Handler 源码解析:nativePollOnce阻塞和na

2021-03-16  本文已影响0人  失足者

收录:

Android Handler机制 - MessageQueue如何处理消息
Handler 如何做到阻塞
Android篇:2019初中级Android开发社招面试解答(中)

Handler消息机制组成:

如何保证looper的唯一性

每个线程只有一个looper,而每个Thread中都又一个关键Threadlocal。是用于存放每个线程的looper对象的,存取的方式是通过get/set。相当于一个map的存放方式。键位key是当前线程的实例。value就是looper对象。所以每次创建looper都会去ThreadLocal里面找有没有当前线程的looper。

如何知道 message 发送到哪个handler处理
当使用 Handler.sendMessage() 发送消息时,调用 enqueueMessage 方法内有 msg.target = this 将 Handler 实例赋值给 msg 对象。当 loop() 取出消息时,调用 dispatchMessage 方法根据 target 属性,回调对应 handler 实例的 handlerMessage 方法处理消息。

具体流程图:
Handler工作流程

源码分析:

  1. 插入消息
    MessageQueue.enqueueMessage:Handler调用sendMessage()发送消息,而Handler内部通过Looper对象得到MessageQueue对象后又调用MessageQueue.enqueueMessage方法。
boolean enqueueMessage(Message msg, long when) {
            ...

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            //插入前先消息队列是否有消息,新的头,如果被阻止,则唤醒事件队列。
            if (p == null || when == 0 || when < p.when) {
                //将消息放进队列头部
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;//指示next()是否被阻止在pollOnce()中以非零超时等待。如果阻塞,则需要唤醒looper
            } 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.
                /*插入队列中间。 通常,除非队列的开头有障碍并且消息是队列中最早的
                  异步消息,否则我们不必唤醒事件队列。(队列中消息不为空,并且next()也没有阻塞)*/
                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;
            }

            // 如果looper阻塞/休眠中,则唤醒looper循环机制处理消息
            if (needWake) {
                nativeWake(mPtr);//唤醒
            }
        }
        return true;
    }

调用nativeWake唤醒(这部分内容出自头部连接,详细源码分析可看前辈的)

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

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

void Looper::wake(){
    ...
    //往mWakeEventFd 中write 1,用以唤醒 looper
    ssize_t mWrite = TEMP_FAILURE_READY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
}

既然有写入消息,那必定要把消息处理掉,所以唤醒了epoll_wait(),然后继续方法调动awoken(),这个方法就是将之前写入的1读出,表示消费这个事件

void Looper::awaken(){
    ...
    //读取头部消息,静默处理掉
    TEMP_FAILURE_READY(read(mWakeEventFd, &counter, sizeof(uint64_t)));
}

随后在Java 层的next()@MessageQueue 就被唤醒,读取在enqueueMessage()@MessageQueue 插在队头的消息进行处理

  1. Looper循环读取消息
    当looper循环机制在MessageQueue的next()读取消息时发现消息队列中没有消息时,就会调用nativePollOnce(ptr, nextPollTimeoutMillis);将next()阻塞在PollOnce中。looper也就进入了休眠期。
@UnsupportedAppUsage
    Message next() {
        // 如果消息循环已经退出并被处理,请返回此处。
        // 如果应用程序尝试退出后不支持的循环程序,则会发生这种情况。
        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();
            }
            //就是在这里根据nextPollTimeoutMillis判断是否要阻塞
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // 尝试检索下一条消息。 如果找到则返回。
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // 被障碍挡住了。 在队列中查找下一条异步消息。
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {//队列中拿到的消息不为null
                    if (now < msg.when) {
                        // 下一条消息尚未准备好。 设置超时以使其在准备就绪时醒来。
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 正常返回处理
                        ...
                } else {
                    // 队列中没有消息,标记阻塞looper循环进入休眠
                    nextPollTimeoutMillis = -1;
                }

                // 现在已处理所有挂起的消息,处理退出消息。
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // 空闲句柄仅在队列为空或将来要处理队列中的第一条消息(可能是屏障)时才运行。
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                ...
            }

            ...

            // 将空闲处理程序计数重置为0,这样我们就不会再次运行它们。
            pendingIdleHandlerCount = 0;

            // 在调用空闲处理程序时,可能已经传递了一条新消息,
            //因此返回并再次查找未处理消息,而无需等待。
            nextPollTimeoutMillis = 0;
        }
    }
上一篇 下一篇

猜你喜欢

热点阅读