💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
#### 10.2.2 消息队列的工作原理 消息队列在Android中指的是[MessageQueue](https://www.androidos.net.cn/android/6.0.1_r16/xref/frameworks/base/core/java/android/os/MessageQueue.java), MessageQueue主要包含两个操作:**插入和读取**。 **读取操作本身会伴随着删除操作**,**插入和读取**对应的方法分别为**enqueueMessage和next**,其中 * **enqueueMessage**的作用是**往消息队列中插入一条消息**, * 而**next的作用是从消息队列中取出一条消息并将其从消息队列中移除**。 尽管MessageQueue叫消息队列,但是**它的内部实现并不是用的队列**,实际上它是通过**一个单链表的数据结构来维护消息列表,单链表在插入和删除上比较有优势**。 MessageQueue是消息机制的Java层和C++层的连接纽带,大部分核心方法都交给native层来处理,其中MessageQueue类中涉及的native方法如下: ``` private native static long nativeInit(); private native static void nativeDestroy(long ptr); private native void nativePollOnce(long ptr, int timeoutMillis); private native static void nativeWake(long ptr); private native static boolean nativeIsPolling(long ptr); private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events); ``` 下面主要看一下它的enqueueMessage和next方法的实现,enqueueMessage的源码如下所示。 ``` boolean enqueueMessage(Message msg, long when) { // 每一个普通Message必须有一个target 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) { if (mQuitting) { //正在退出时,回收msg,加入到消息池 msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { //p为null(代表MessageQueue没有消息) 或者msg的触发时间是队列中最早的, 则进入该该分支 msg.next = p; mMessages = msg; needWake = mBlocked; //当阻塞时需要唤醒 } else { //将消息按时间顺序插入到MessageQueue。一般地,不需要唤醒事件队列,除非 //消息队头存在barrier,并且同时Message是队列中最早的异步消息。 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; prev.next = msg; } //消息没有退出,我们认为此时mPtr != 0 if (needWake) { nativeWake(mPtr); } } return true; } ``` 从**enqueueMessage**的实现来看,它的**主要操作其实就是单链表的插入操作**,这里就不再过多解释了, 下面看一下next方法的实现,next的主要逻辑如下所示。 ``` Message next() { final long ptr = mPtr; if (ptr == 0) { //当消息循环已经退出,则直接返回 return null; } int pendingIdleHandlerCount = -1; // 循环迭代的首次为-1 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; //当消息的Handler为空时,则查询异步消息 if (msg != null && msg.target == null) { //当查询到异步消息,则立刻退出循环 do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { //当异步消息触发时间大于当前时间,则设置下一次轮询的超时时长 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // 获取一条消息,并返回 mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; //设置消息的使用状态,即flags |= FLAG_IN_USE msg.markInUse(); return msg; //成功地获取MessageQueue中的下一条即将要执行的消息 } } else { //没有消息 nextPollTimeoutMillis = -1; } //消息正在退出,返回null if (mQuitting) { dispose(); return null; } //当消息队列为空,或者是消息队列的第一个消息时 if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { //没有idle handlers 需要运行,则循环并等待。 mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } //只有第一次循环时,会运行idle handlers,执行完成后,重置pendingIdleHandlerCount为0. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; //去掉handler的引用 boolean keep = false; try { keep = idler.queueIdle(); //idle时执行的方法 } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } //重置idle handler个数为0,以保证不会再次重复运行 pendingIdleHandlerCount = 0; //当调用一个空闲handler时,一个新message能够被分发,因此无需等待可以直接查询pending message. nextPollTimeoutMillis = 0; } } ``` 可以发现**next方法是一个无限循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞在这里**。 当**有新消息到来时,next方法会返回这条消息并将其从单链表中移除**。