Handler消息机制全解(二)消息的睡眠等待

    在上面的分析中,我们知道loop()方法会循环调用MessageQueue的next()方法来获取要处理的消息,并且会在改方法处睡眠等待,那么是如何睡眠的呢,让我们来分析next()方法中的原理,下面是MessageQueue类中的相关核心代码。

public final class MessageQueue {
    ......
    // True if the message queue can be quit.
    private final boolean mQuitAllowed;

    @SuppressWarnings("unused")
    private long mPtr; // used by native code

    Message mMessages;
    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
    ......
    private IdleHandler[] mPendingIdleHandlers;
    private boolean mQuitting;

    // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
    private boolean mBlocked;

    ......
    private native static long nativeInit();
    private native static void nativeDestroy(long ptr);
    private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
    private native static void nativeWake(long ptr);
    ......

    // Disposes of the underlying message queue.
    // Must only be called on the looper thread or the finalizer.
    private void dispose() {
        if (mPtr != 0) {
            nativeDestroy(mPtr);
            mPtr = 0;
        }
    }

    ......
        
    public void addIdleHandler(@NonNull IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }

    public void removeIdleHandler(@NonNull IdleHandler handler) {
        synchronized (this) {
            mIdleHandlers.remove(handler);
        }
    }

    ......
    (1)next方法说明
    Message next() {
        ......

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            (2)检查线程是否要睡眠等待
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                (3)检查并获取一个有效消息
                // Try to retrieve the next message.  Return if found.
                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());
                }
                (4)计算消息的等待时间
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        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 {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                (5)检查是否结束线程消息循环
                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                (6)检查是否处理线程空闲消息事件
                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            (7)处理线程空闲消息事件
            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            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;
        }
    }

    void quit(boolean safe) {
        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);
        }
    }

    ......
}
   (1)next方法说明:next方法主要是用来获取一个线程消息,首先会开启一个for无限循环,在循环过程中主要依次执行以下逻辑,根据线程等待时间检查是否需要睡眠等待( 刚开始进入next方法时线程不需要睡眠),接着获取有效消息,计算消息等待的时间,检查是否要处理 线程空闲消息,检查是否退出消息循环,处理线程空闲消息,最后,执行完以上逻辑后会再得到一个线程等待时间,再次循环来判断线程是否需睡眠等待,如此往复直到获取一个消息返回,跳出循环。为什么要开启一个循环来获取消息呢,因为线程在睡眠等待过程中,一旦有其他消息事件发送过来需要处理,此时线程将会被唤醒返回,但是此时不能立马就返回一个消息,而是需要重新计算新消息的等待时间,所以要重复以上逻辑。
     (2)检查线程是否要睡眠等待:首先 在循环中调用Native层的 nativePollOnce()方法来检查是否需要睡眠等待,nativePollOnce方法中需要传入两个参数,ptr参数代表的是Native层中NativeMessageQueue对象指针,用来找到Native层的NativeMessageQueue对象,并调用该对象中的核心方法来判断当前线程是否要睡眠等待。其中,nextPollTimeoutMillis参数就是用来表示睡眠等待的时间,如果nextPollTimeoutMillis等于-1,表示线程会无限的睡眠下去,nextPollTimeoutMillis等于0表示不睡眠立马返回,nextPollTimeoutMillis大于0表示线程需要睡眠等待nextPollTimeoutMillis时间后返回。除了以上等待时间到了后线程会被唤醒返回外,如果外界调用了Native层的nativeWake()方法也会立马唤醒返回,而nativeWake方法的调用时机经常是其他线程向该睡眠等待的线程消息队列中发来了一个消息需要立马处理,比如一个触摸事件等,此时会调用nativeWake方法来唤醒线程,后面会分析消息的发送原理。因为每次 调用next()方法时nextPollTimeoutMillis被赋值为0,所以刚开始调用 nativePollOnce方法时会立马返回,因为每次调用next方法取得一个消息时,需要先计算该消息的处理等待时间,根据时间再判断是否要睡眠。
     (3)检查并获取一个有效消息: 首先取得当前消息队列中的消息mMessages赋值给变量msg,其中, mMessages使用的是链表数据结构来存储所有的消息,接着循环检查并取得链表最前面的一个有效消息。
     (4)计算消息的等待时间: 取得消息完成后会先判断该消息msg是否为null, 如果消息不为null,说明有消息发来需要处理,此时要判断该消息的处理等待时间,分两种情况。如果消息的处理时间>当前时间,说明消息在将来某个时间处理,此时计算该消息需要等待的时间,赋值给nextPollTimeoutMillis。否则,消息需要立马处理,此时将mBlocked 设置为false,并返回该消息,跳出循环,mBlocked 为false说明,线程是在处理消息事件,不在休眠。如果消息为null,说明此时线程还没有消息需要处理,将nextPollTimeoutMillis设置为-1,线程将会无限睡眠下去。
     (5)检查是否结束线程消息循环:如果逻辑执行到这里,说明线程需要睡眠等待,此时先判断是否需要退出线程消息循环,只有调用了quit()方法,才有可能会退出循环,因为主线程消息循环没法退出。
     (6)检查是否处理线程空闲消息事件:如果 线程需要睡眠等待,则先判断此次消息在线程将要睡眠等待前, 是否需要处理线程空闲消息,如果pendingIdleHandlerCount<0则不需要处理线程空闲消息,那么就将 mBlocked 设置为true,意思是线程将要进入睡眠等待状态,接着继续循环调用 nativePollOnce,此时的nextPollTimeoutMillis已经被设置为-1或者大于0的值,线程进入睡眠等待。
     (7)处理线程空闲消息事件:如果需要处理线程空闲消息,则会循环mPendingIdleHandlers数组中的所有IdleHandler对象,并调用该对象的queueIdle()方法进行空闲消息的处理。处理完线程空闲消息后会将pendingIdleHandlerCount 重置为0,也就是说一次next()方法中只会执行一次线程空闲消息, 同时nextPollTimeoutMillis 也被设置为0,因为线程空闲消息的处理也需要时间,等处理完线程空闲消息后,说不定其他的消息等待时间已经到了,所以此时不能睡眠,要立即返回继续计算消息的处理时间,判断是否可以处理或者睡眠。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值