美文网首页
Handler问题问答

Handler问题问答

作者: 风月寒 | 来源:发表于2020-08-19 16:55 被阅读0次
    子线程维护的Looper,消息队列没有消息时的处理方方案怎么处理

    首先调用Looper的loop(),在loop()中,会去一直从MessageQueue中获取message.

    Message msg = queue.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);  //1
    
                synchronized (this) {
                    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) {
                        .......
                    } else {
                        // No more messages.
                        nextPollTimeoutMillis = -1;   //2
                    }
    
                    // Process the quit message now that all pending messages have been handled.
                    if (mQuitting) {
                        dispose();
                        return null;
                    }   //3
    
                    ......
                    if (pendingIdleHandlerCount <= 0) {
                        // No idle handlers to run.  Loop and wait some more.
                        mBlocked = true;
                        continue;
                    }  // 4
                }
            }
        }
    
    

    主要分析上面的1、2、3、4处。

    从上面可以看到,当没有message的时候,也就是会走2处,会把nextPollTimeoutMillis = -1,
    当nextPollTimeoutMillis = -1时,走到4的时候,此时会结束本次循环,继续下次的循环,然后走到1处,

    private native void nativePollOnce(long ptr, int timeoutMillis);
    

    当nativePollOnce()的timeoutMillis参数取值如下:

    0,立即返回,没有阻塞;

    负数,一直阻塞,直到事件发生;

    正数,表示最多等待多久时间;

    而我们传入的nextPollTimeoutMillis = -1,此时会一直阻塞。

    在这里面采用的时==epoll机制==。

    什么时候会唤醒

    当我们调用enqueueMessage时,也就是插入消息的时候,此时会进行唤醒。

    boolean enqueueMessage(Message msg, long when) {
            if (msg.target == null) {
                throw new IllegalArgumentException("Message must have a target.");
            }
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }
    
            synchronized (this) {
                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                boolean needWake;
                if (p == null || when == 0 || when < p.when) {
                    // New head, wake up the event queue if blocked.
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked;//1
                } else {//3
                    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);
                }//2
            }
            return true;
        }
    

    我们可以知道mBlocked赋值是在next方法中,当获取到一个message的时候,mBlocked = false,表明此时没有阻塞,不需要唤醒。

    if (pendingIdleHandlerCount <= 0) {
                        // No idle handlers to run.  Loop and wait some more.
                        mBlocked = true;
                        continue;
                    }
    

    pendingIdleHandlerCount时用来统计IdleHandler的数量,当没有IdleHandler在运行,说明 Loop正在阻塞中,此时mBlocked = true;

    从1我们可以知道,p == null || when == 0 || when < p.when表示此时队列中没有消息,此时添加的消息是第一个,那next()中的此时是阻塞的,此时mBlocked = true,则needWake = true,插入成功之后,走2处代码,则去唤醒。

    当走3处的代码的时候,表明此时是有message的,我们则根据执行时间的先后来进行插入。此时needWake = mBlocked && p.target == null && msg.isAsynchronous();

    mBlocked = false,则needWake = false;最后不走后面唤醒的逻辑。

    如果一直不插入消息,我们该怎么唤醒?

    调用Loop的quit();

    public void quit() {
            mQueue.quit(false);
        }
    
    

    然后会调用MessageQueue的quit();

    void quit(boolean safe) {
            synchronized (this) {
                if (mQuitting) {
                    return;
                }
                mQuitting = true;
    
                if (safe) {
                    removeAllFutureMessagesLocked();
                } else {
                    removeAllMessagesLocked();
                }
    
                // We can assume mPtr != 0 because mQuitting was previously false.
                nativeWake(mPtr);
            }
        }
    

    调用quit()是,将mQuitting = true;然后调用nativeWake(mPtr)进行唤醒。

    唤醒之后,再看next(),

    if (mQuitting) {
                        dispose();
                        return null;
                    }
    
    

    因为在quit()中已经将mQuitting置为true,此时则会返回null.

    然后再Loop的loop()中的到的message = null.

    if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
    

    可以看到当msg == null时,直接返回。

    相关文章

      网友评论

          本文标题:Handler问题问答

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