💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
当一切准备就绪后,Java层的消息循环处理,也就是Looper会在一个循环中提取并处理消息。消息的提取就是调用MessageQueue的next()方法。当消息队列为空时,next就会阻塞。MessageQueue同时支持Java层和Native层的事件,那么其next()方法该怎么实现呢?具体代码如下: **MessagQueue.java-->MessageQueue.next()** ``` final Message next() { int pendingIdleHandlerCount = -1; int nextPollTimeoutMillis = 0; for (;;) { ...... // mPtr保存了NativeMessageQueue的指针,调用nativePollOnce进行等待 nativePollOnce(mPtr,nextPollTimeoutMillis); synchronized (this) { final long now = SystemClock.uptimeMillis(); // mMessages用来存储消息,这里从其中取一个消息进行处理 final Message msg = mMessages; if (msg != null) { final long when = msg.when; if (now >= when) { mBlocked = false; mMessages = msg.next; msg.next = null; msg.markInUse(); return msg; // 返回一个Message给Looper进行派发和处理 } else { nextPollTimeoutMillis = (int) Math.min(when- now, Integer.MAX_VALUE); } } else { nextPollTimeoutMillis = -1; } ...... /* 处理注册的IdleHandler,当MessageQueue中没有Message时, Looper会调用IdleHandler做一些工作,例如做垃圾回收等 */ ...... pendingIdleHandlerCount = 0; nextPollTimeoutMillis = 0; } } } ``` 看到这里,可能会有人觉得这个MessageQueue很简单,不就是从以前在Java层的wait变成现在Native层的wait了吗?但是事情本质比表象要复杂得多,来思考下面的情况: nativePollOnce()返回后,next()方法将从mMessages中提取一个消息。也就是说,要让nativePollOnce()返回,至少要添加一个消息到消息队列,否则nativePollOnce()不过是做了一次无用功罢了。 如果nativePollOnce()将在Native层等待,就表明Native层也可以投递Message,但是从Message类的实现代码上看,该类和Native层没有建立任何关系。那么nativePollOnce()在等待什么呢? 对于上面的问题,相信有些读者心中已有了答案:nativePollOnce()不仅在等待Java层来的Message,实际上还在Native还做了大量的工作。 下面我们来分析Java层投递Message并触发nativePollOnce工作的正常流程。 #### 1. 在Java层投递Message MessageQueue的enqueueMessage函数完成将一个Message投递到MessageQueue中的工作,其代码如下: **MesssageQueue.java-->MessageQueue.enqueueMessage()** ``` final boolean enqueueMessage(Message msg, longwhen) { ...... finalboolean needWake; synchronized (this) { if(mQuiting) { return false; }else if (msg.target == null) { mQuiting = true; } msg.when = when; Message p = mMessages; if(p == null || when == 0 || when < p.when) { /* 如果p为空,表明消息队列中没有消息,那么msg将是第一个消息,needWake 需要根据mBlocked的情况考虑是否触发 */ msg.next= p; mMessages = msg; needWake = mBlocked; } else { // 如果p不为空,表明消息队列中还有剩余消息,需要将新的msg加到消息尾 Message prev = null; while (p != null && p.when <= when) { prev = p; p = p.next; } msg.next = prev.next; prev.next = msg; // 因为消息队列之前还剩余有消息,所以这里不用调用nativeWakeup needWake = false; } } if(needWake) { // 调用nativeWake,以触发nativePollOnce函数结束等待 nativeWake(mPtr); } returntrue; } ``` 上面的代码比较简单,主要功能是: - 将message按执行时间排序,并加入消息队。 - 根据情况调用nativeWake函数,以触发nativePollOnce函数,结束等待。 * * * * * **建议** 虽然代码简单,但是对于那些不熟悉多线程的读者,还是要细细品味一下mBlocked值的作用。我们常说细节体现美,代码也一样,这个小小的mBlocked正是如此。 * * * * * #### 2. nativeWake函数分析 nativeWake函数的代码如下所示: **android_os_MessageQueue.cpp-->android_os_MessageQueue_nativeWake()** ``` static voidandroid_os_MessageQueue_nativeWake(JNIEnv* env, jobject obj, jint ptr) { NativeMessageQueue* nativeMessageQueue = // 取出NativeMessageQueue对象 reinterpret_cast<NativeMessageQueue*>(ptr); returnnativeMessageQueue->wake(); // 调用它的wake函数 } [android_os_MessageQueue.cpp-->NativeMessageQueue::wake()] void NativeMessageQueue::wake() { mLooper->wake(); // 层层调用,现在转到mLooper的wake函数 } ``` Native Looper的wake函数代码如下: **Looper.cpp-->Looper::wake()** ``` void Looper::wake() { ssize_tnWrite; do { // 向管道的写端写入一个字符 nWrite = write(mWakeWritePipeFd, "W", 1); } while(nWrite == -1 && errno == EINTR); } ``` Wake()函数则更为简单,仅仅向管道的写端写入一个字符”W”,这样管道的读端就会因为有数据可读而从等待状态中醒来。