美文网首页
Handler 源码解析:nativePollOnce阻塞和na

Handler 源码解析:nativePollOnce阻塞和na

作者: 失足者 | 来源:发表于2021-03-16 17:12 被阅读0次

    收录:

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

    Handler消息机制组成:

    • Message(消息):需要被传递的消息,消息分为硬件产生的消息(如按钮、触摸)和软件生成的消息。
    • MessageQueue(消息队列):负责消息的存储与管理,负责管理由 Handler发送过来的Message。读取后会自动删除消息,单链表维护,插入和删除上有优势。在其next()方法中会无限循环,不断判断是否有消息,有就返回这条消息并移除。
    • Handler(消息处理器):负责Message的发送及处理。主要向消息池发送各种消息事件(Handler.sendMessage())和处理相应消息事件(Handler.handleMessage()),按照先进先出执行,内部使用的是单链表的结构。
    • Looper(消息池/循环机制):负责关联线程以及消息的分发,在该线程下从 MessageQueue获取 Message,分发给Handler,Looper创建的时候会创建一个 MessageQueue,调用loop()方法的时候消息循环开始,其中会不断调用messageQueue的next()方法,当有消息就处理,否则阻塞在messageQueue的next()方法中。当Looper的quit()被调用的时候会调用messageQueue的quit(),此时next()会返回null,然后loop()方法也就跟着退出。

    如何保证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工作流程
    • App启动时创建一个主线程(UI),接着UI线程会创建一个Looper,同时也会在在Looper内部创建一个消息队列。而在创键Handler的时候取出当前线程的Looper,并通过该Looper对象获得消息队列(MessageQueue),然后Handler在子线程中通过MessageQueue.enqueueMessage在消息队列中添加一条Message。
    • 通过Looper.loop() 开启消息循环不断轮询调用 MessageQueue.next(),取得对应的Message并且通过Handler.dispatchMessage传递给Handler,最终调用Handler.handlerMessage处理消息。

    源码分析:

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

    相关文章

      网友评论

          本文标题:Handler 源码解析:nativePollOnce阻塞和na

          本文链接:https://www.haomeiwen.com/subject/koswihtx.html