Android Handler消息机制详解4

737 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第15天,点击查看活动详情

b.quit()

void quit(boolean safe) {
    //主线程的MessageQueue不允许退出
    if (!mQuitAllowed) {
        throw new IllegalStateException("Main thread not allowed to quit.");
    }
    //加入同步锁,如果正在退出则直接返回
    synchronized (this) {
        if (mQuitting) {
            return;
        }
        mQuitting = true;

        if (safe) {
            removeAllFutureMessagesLocked();
        } else {
            removeAllMessagesLocked();
        }

        // We can assume mPtr != 0 because mQuitting was previously false.
        //唤醒锁
        nativeWake(mPtr);
    }
}

      当执行Looper.quit()后,实际执行的是Message.quit(),在quit()里面设置mQuitting=true,最后执行nativeWake(mPtr)来唤醒next(),最终返回message为null,然后Looper.loop()里面检测到获取到的message为null,直接退出loop()。

c.next()

Message next() {
    .........
    for (;;) {
        //-------------------分析点1---------------------------
        nativePollOnce(ptr, nextPollTimeoutMillis);
        //-------------------分析点2---------------------------
        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            //-------------------分析点3---------------------------
            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) {
                if (now < msg.when) {
                    //-------------------分析点4---------------------------
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    //-------------------分析点5---------------------------
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
               }
            } else {
                //queue里面没消息时,就将nextPollTimeoutMillis置为-1,一直等待中
                nextPollTimeoutMillis = -1;
            }
        // Process the quit message now that all pending messages have been handled.
        //当执行quit()时,会执行以下逻辑
        if (mQuitting) {
            dispose();
            //返回的message为null,Looper.loop()中的Message msg = queue.next()接收到message为null,直接return,从而退出loop()
            return null;
        }
        ........ 
        ........
        // Run the idle handlers.
        // We only ever reach this code block during the first iteration.
        //-------------------分析点6---------------------------
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
}

      从上面代码可以看到,主要的点为:

      分析点1:nativePollOnce(ptr, nextPollTimeoutMillis)是native方法,最终会调用到Linux的epoll_wait()进行阻塞等待,nextPollTimeoutMillis是等待时间,当消息队列中没有消息时,就一直等待,直到通过nativeWeak()来唤醒;有消息但是没有到消息的执行时间时,需要等待delay时间,然后执行下面的逻辑;

      分析点2:加入synchronized (this)来确保取message的正确性;

      分析点3:消息屏障,即:msg.target == null,只能通过postSyncBarrier()来插入屏障消息,不能通过enqueueMessage()(会判断msg.target==null,直接抛异常),当消息队列头是屏障信息时,会循环找到消息队列中的异步消息(asynchronous),如果找到,执行分析点4;如果未找到的话,会一直休眠,等待唤醒;

    public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

    private int postSyncBarrier(long when) {
        // Enqueue a new sync barrier token.
        // We don't need to wake the queue because the purpose of a barrier is to stall it.
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

      从暴露的方法来看是hide及private的,应用是调用不到的,除非通过反射调用,从hide的方法可以看到,调用该方法后,会将该屏障消息置于消息队列的头部,因为SystemClock.uptimeMillis()肯定小于当前时间的,然后返回token;
前面分析到,在消息队列中有消息屏障后,只会执行队列中的异步消息,那剩余的普通消息什么时候能够被执行呢?移除屏障消息:

    public void removeSyncBarrier(int token) {
        // Remove a sync barrier token from the queue.
        // If the queue is no longer stalled by a barrier then wake it.
        synchronized (this) {
            Message prev = null;
            Message p = mMessages;
            while (p != null && (p.target != null || p.arg1 != token)) {
                prev = p;
                p = p.next;
            }
            if (p == null) {
                throw new IllegalStateException("The specified message queue synchronization "
                        + " barrier token has not been posted or has already been removed.");
            }
            final boolean needWake;
            //----------------------判断a----------------------------
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            //----------------------判断b----------------------------
            } else {
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            p.recycleUnchecked();

            // If the loop is quitting then it is already awake.
            // We can assume mPtr != 0 when mQuitting is false.
            if (needWake && !mQuitting) {
                nativeWake(mPtr);
            }
        }
    }

      根据上面返回的token来移除屏障消息,然后判断是否需要执行nativeWake()来唤醒next(),来执行队列中的普通消息,判断是否需要执行nativeWake()条件如下:

      判断a:prev不为null,说明屏障消息不处于mMessages队列头,即还未执行到,不必执行唤醒;

      判断b:prev为null,说明屏障消息处于mMessages队列头,如果mMessages中有普通消息的话肯定未执行,因此来判断如果next是普通消息或mMessages为空时,进行nativeWeak();如果不为null,且next还是屏障信息,不需要nativeWake()。

      分析点4:当queue头的message未到执行时间时,会计算出nextPollTimeoutMillis,最后通过nativePollOnce(ptr, nextPollTimeoutMillis)等待时间到来执行message或新的nativeWeak();

      分析点5:从queue头取出message,然后queue头指向下一个执行的message,最后返回取出的message;

      分析点6:当队列中下一个需要执行的消息还未到时间(即需要等待nextPollTimeoutMillis),或消息队列中没有需要执行的消息(nextPollTimeoutMillis=-1),此时可以执行IdleHandler,即空闲的Handler,场景:1.延迟执行,没有固定时间;2.批量任务,任务密集,且只关注最终结果;使用方式如下:

     Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
        @Override
        public boolean queueIdle() {
            //do something
            return false;
        }
     });