Handler问题问答

子线程维护的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问题问答)