该文章属于《Android Handler机制之》系列文章,若是想了解更多,请点击 《Android Handler机制之总目录》android
在前面的文章中,咱们已经大概了解了ThreadLocal的内部原理,以及Handler发消息的大概流程。若是小伙伴若是对Handler机制不熟,建议阅读《Android Handler机制之ThreadLocal》与《Android Handler机制之Handler 、MessageQueue 、Looper》。该篇文章主要着重讲解Message的发送与取出的具体逻辑细节。在此以前,咱们先回顾一下Handler发送消息的具体流程。 bash
咱们都知道当调用Handler发送消息的时候,不论是调用sendMessage,sendEmptyMessage,sendMessageDelayed仍是其余发送一系列方法。最终都会调用sendMessageDelayed(Message msg, long delayMillis)
方法。异步
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
复制代码
该方法会调用sendMessageAtTime()方法。其中第二个参数是执行消息的时间,是经过从开机到如今的毫秒数(手机睡眠的时间不包括在内)+ 延迟执行的时间。这里不使用System.currentTimeMillis() ,是由于该时间是能够修改的。会致使延迟消息等待时间不许确。该方法内部会调用sendMessageAtTime()方法,咱们接着往下走。async
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
复制代码
这里获取到线程中的MessageQueue对象mQueue(在构造函数经过Looper得到的),并调用enqueueMessage()方法,enqueueMessage()方法最终内部会调用MessageQueue的enqueueMessage()方法,那如今咱们就直接看MessageQueue中把消息加入消息队列中的方法。ide
当经过handler调用一系列方法如sendMessage()、sendMessageDelayed()方法时,最终会调用MessageQueue的enqueueMessage()方法。如今咱们就来看看,消息是怎么加入MessageQueue(消息队列)中去的。函数
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) {
//若是当前消息循环已经结束,直接退出
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
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;
} else {
//判断唤醒条件,当前当前消息队列头部消息是屏障消息,且当前插入的消息为异步消息
//且当前消息队列处于无消息可处理的状态
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;
}
//调用nativeWake,以触发nativePollOnce函数结束等待
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
复制代码
这里你们确定注意到了nativeWake()方法,这里先不对该方法进行详细的描述,下文会对其解释及介绍。 其实经过代码你们就应该发现了,在将消息加入到消息队列中时,已经将消息按照等待时间进行了排序。排序分为两种状况(这里图中的message.when是与当前的系统的时间差):oop
综上,咱们了解了在咱们使用Handler发送消息时,当消息进入到MessageQueue(消息队列)中时,已经按照等待时间进行了排序,且其头部对应的消息是Loop即将取出的消息。post
咱们都知道消息的取出,是经过Loooper.loop()方法,其中loop()方法内部会调用MessageQueue中的next()方法。那下面咱们就直接来看next()方法。ui
Message next() {
//若是退出消息消息循环,那么就直接退出
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//执行native层消息机制层,
//timeOutMillis参数为超时等待时间。若是为-1,则表示无限等待,直到有事件发生为止。
//若是值为0,则无需等待当即返回。该方法可能会阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
//获取系统开机到如今的时间,若是使用System.currentMillis()会有偏差,
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;//头部消息
//判断是不是栅栏,同时获取消息队列最近的异步消息
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//获取消息,判断等待时间,若是还须要等待则等待相应时间后唤醒
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 {
// 不须要等待时间或者等待时间已经到了,那么直接返回该消息
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 {
//没有更多的消息了
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
//判断是否已经退出了
if (mQuitting) {
dispose();
return null;
}
// 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.
//获取空闲时处理任务的handler 用于发现线程什么时候阻塞等待更多消息的回调接口。
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
//若是空闲时处理任务的handler个数为0,继续让线程阻塞
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
//判断当前空闲时处理任务的handler是不是为空
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
//只有第一次迭代的时候,才会执行下面代码
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);
}
}
}
// 重置空闲的handler个数,由于不须要重复执行
pendingIdleHandlerCount = 0;
//当执行完空闲的handler的时候,新的native消息可能会进入,因此唤醒Native消息机制层
nextPollTimeoutMillis = 0;
}
}
复制代码
这里你们直接看MessageQueue的next()法确定会很懵逼。妈的,这个nativePollOnce()方法是什么鬼,为毛它会阻塞呢?这个msg.isAsynchronous()判断又是怎么回事?妈的这个逻辑有点乱理解不了啊。你们不要慌,让咱们带着这几个问题来慢慢分析。this
其实在Android 消息处理机制中,不只包括了Java层的消息机制处理,还包括了Native消息处理机制(与咱们知道的Handler机制同样,也拥有Handler、Looper、MessageQueue)。这里咱们不讲Native消息机制的具体代码细节,若是有兴趣的小伙伴,请查看----->深刻理解Java Binder和MessageQueue
这里咱们用一张图来表示Native消息与Jave层消息的关系(这里为你们提供了Android源码,你们能够按需下载),具体细节以下图所示:
其实咱们也能够从Java层中的MessageQueue中几个方法就能够看出来。其中声明了几个本地的方法。
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);
复制代码
特别是在MessageQueue构造方法中。
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();//mPtr 实际上是Native消息机制中MessageQueue的地址。
}
复制代码
在Java层中MessageQueue在初始化的时候,会调用本地方法去建立Native MessageQueue。并经过mPrt保存了Native中的MessageQueue的地址。
想知道有什么关系,咱们须要查看frameworks\base\core\jni\android_os_MessageQueue.cpp文件,
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
}
复制代码
其实nativeInit()方法很简单,初始化NativeMessageQueue对象而后返回其地址。如今咱们继续查看NativeMessageQueue的构造函数。
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
复制代码
哇,咱们看见了咱们熟悉的"Looper",这段代码其实很好理解。Native Looper调用静态方法getForThread(),获取当前线程中的Looper对象。若是为空,则建立Native Looper对象。这里你们确定会有个疑问。当前线程是指什么线程呢?想知道到底绑定是什么线程,咱们须要进入Native Looper中查看setForThread()与getForThread()两个方法。
/**
* Returns the looper associated with the calling thread, or NULL if
* there is not one.
*/
sp<Looper> Looper::getForThread() {
int result = pthread_once(& gTLSOnce, initTLSKey);
LOG_ALWAYS_FATAL_IF(result != 0, "pthread_once failed");
return (Looper*)pthread_getspecific(gTLSKey);
}
复制代码
这里pthread_getspecific()机制相似于Java层的ThreadLocal中的get()方法,是从线程中获取key值对应的数据。其中经过咱们能够经过注释就能明白,Native Looper是存储在本地线程中的,而对应的线程,就是调用它的线程,而咱们是在主线程中调用的。故Native Looper与主线程产生了关联。那么相应的setForThread()也是对主线程进行操做的了。接着看setForThread()方法。
/**
* Sets the given looper to be associated with the calling thread.
* If another looper is already associated with the thread, it is replaced. *
* If "looper" is NULL, removes the currently associated looper.
*/
void Looper::setForThread(const sp<Looper>& looper) {
sp<Looper> old = getForThread(); // also has side-effect of initializing TLS
if (looper != NULL) {
looper->incStrong((void*)threadDestructor);
}
pthread_setspecific(gTLSKey, looper.get());
if (old != NULL) {
old->decStrong((void*)threadDestructor);
}
}
复制代码
这里pthread_setspecific()机制相似于Java层的ThreadLocal中的set()方法。经过注释咱们明白将Native looper放入调用线程,若是已经存在,就替换。若是为空就删除。
通过上文的讨论与分析,你们如今已经知道了,在Android消息机制中不只有 Java层的消息机制,还有Native的消息机制。既然要出里Native的消息机制。那么确定有一个处理消息的方法。那么调用本地消息机制消息的方法必然就是nativePollOnce()方法。
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
复制代码
在nativePollOnce()方法中调用nativeMessageQueue的pollOnce()方法,咱们接着走。
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
复制代码
这里咱们发现pollOnce(timeoutMillis)内部调用的是Natave looper中的 pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData)方法。继续看。
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
while (mResponseIndex < mResponses.size()) {
const Response& response = mResponses.itemAt(mResponseIndex++);
int ident = response.request.ident;
if (ident >= 0) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - returning signalled identifier %d: "
"fd=%d, events=0x%x, data=%p",
this, ident, fd, events, data);
#endif
if (outFd != NULL) *outFd = fd;
if (outEvents != NULL) *outEvents = events;
if (outData != NULL) *outData = data;
return ident;
}
}
if (result != 0) {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - returning result %d", this, result);
#endif
if (outFd != NULL) *outFd = 0;
if (outEvents != NULL) *outEvents = 0;
if (outData != NULL) *outData = NULL;
return result;
}
result = pollInner(timeoutMillis);
}
}
复制代码
因为篇幅的限制,这里就简单介绍一哈pollOnce()方法。该方法会一直等待Native消息,其中 timeOutMillis参数为超时等待时间。若是为-1,则表示无限等待,直到有事件发生为止。若是值为0,则无需等待当即返回。 那么既然nativePollOnce()方法有可能阻塞,那么根据上文咱们讨论的MessageQueue中的enqueueMessage中的nativeWake()方法。你们就应该了然了。nativeWake()方法就是唤醒Native消息机制再也不等待消息而直接返回。
到了这里,其实你们都会有个疑问,若是当前主线程的MessageQueue没有消息时,程序就会便阻塞在loop的queue.next()中的nativePollOnce()方法里,一直循环那么主线程为何不卡死呢?这里就涉及到Linux pipe/epoll机制,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,经过往pipe管道写端写入数据来唤醒主线程工做。这里采用的epoll机制,是一种IO多路复用机制,能够同时监控多个描述符,当某个描述符就绪(读或写就绪),则马上通知相应程序进行读或写操做,本质同步I/O,即读写是阻塞的。 因此说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。--摘自Gityuan知乎回答
若是你们想消息了解Native 消息机制的处理机制,请查看----->深刻理解Java Binder和MessageQueue
###屏障消息与异步消息
在next()方法中,有一个屏障的概念(message.target ==null为屏障消息),以下代码:
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());
}
复制代码
其实经过代码,当出现屏障的时候,会滤过同步消息,而是直接获取其中的异步消息并返回。以下图所示:
在Hadnler无参的构造函数中,默认设置的消息都是同步的。
那咱们就能够知道在Android中消息分为了两种,一种是同步消息,另外一种是异步消息。在官方的解释中,异步消息一般表明着中断,输入事件和其余信号,这些信号必须独立处理,即便其余工做已经暂停。
要设置异步消息,直接调用Message的setAsynchronous()方法,方法以下:
/**
* Sets whether the message is asynchronous, meaning that it is not
* subject to {@link Looper} synchronization barriers.
* <p>
* Certain operations, such as view invalidation, may introduce synchronization
* barriers into the {@link Looper}'s message queue to prevent subsequent messages * from being delivered until some condition is met. In the case of view invalidation, * messages which are posted after a call to {@link android.view.View#invalidate} * are suspended by means of a synchronization barrier until the next frame is * ready to be drawn. The synchronization barrier ensures that the invalidation * request is completely handled before resuming. * </p><p> * Asynchronous messages are exempt from synchronization barriers. They typically * represent interrupts, input events, and other signals that must be handled independently * even while other work has been suspended. * </p><p> * Note that asynchronous messages may be delivered out of order with respect to * synchronous messages although they are always delivered in order among themselves. * If the relative order of these messages matters then they probably should not be * asynchronous in the first place. Use with caution. * </p> * * @param async True if the message is asynchronous. * * @see #isAsynchronous() */ public void setAsynchronous(boolean async) { if (async) { flags |= FLAG_ASYNCHRONOUS; } else { flags &= ~FLAG_ASYNCHRONOUS; } } 复制代码
你们能够看到,设置异步消息,官方文档对其有详细的说明,侧面体现出了异步消息的重要性,那下面我就带着你们一块儿来理一理官方的注释说明。
这里我就直接经过ActivityThread中的几个异步消息给你们作一些简单介绍。这里我就不用代码展现了,用图片来表示更清晰明了。
那么Android中在哪些状况下会发生屏障消息呢?其实最为常见的就是在咱们界面进行绘制的时候,如在ViewRootImpl.scheduleTraversals()中。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//发送屏障消息
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
复制代码
在调用scheduleTraversals()方法时,咱们发现会发生一个屏障过去。具体代码以下:
private int postSyncBarrier(long when) {
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;
}
}
复制代码
这里咱们直接将围栏放在了消息队列中,同时重要的是咱们并无直接设置target,也就是tartget =null。其实如今咱们能够想象,咱们当咱们正在进行界面的绘制的时候,咱们是不但愿有其余操做的,这个时候,要排除同步消息操做,也是可能理解的。
在MessageQueue中的next()方法,出现了IdleHandler(MessageQueuqe空闲时执行的任务),查看MessageQueue中IdleHander接口的说明:
/**
* Callback interface for discovering when a thread is going to block
* waiting for more messages.
*/
public static interface IdleHandler {
/**
* Called when the message queue has run out of messages and will now
* wait for more. Return true to keep your idle handler active, false
* to have it removed. This may be called if there are still messages
* pending in the queue, but they are all scheduled to be dispatched
* after the current time.
*/
boolean queueIdle();
}
复制代码
当线程正在等待更多消息时,会回调该接口,同时queueIdl()方法,会在消息队列执行完全部的消息等待且在等待更多消息时会被调用,若是返回true,表示该任务会在消息队列等待更多消息的时候继续执行,若是为false,表示该任务执行完成后就会被删除,再也不执行。
其中MessageQueue经过使用addIdleHandler(@NonNull IdleHandler handler) 方法添加空闲时任务。具体代码以下:
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
复制代码
既然MessageQueue能够添加空闲时任务,那么咱们就看看最为明显的ActivityThread中声明的GcIdler。在ActivityThread中的H收到GC_WHEN_IDLE消息后,会执行scheduleGcIdler,将GcIdler添加到MessageQueue中的空闲任务集合中。具体以下:
void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
//添加GC任务
Looper.myQueue().addIdleHandler(mGcIdler);
}
mH.removeMessages(H.GC_WHEN_IDLE);
}
复制代码
ActivityThread中GcIdler的详细声明:
//GC任务
final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
//执行后,就直接删除
return false;
}
}
// 判断是否须要执行垃圾回收。
void doGcIfNeeded() {
mGcIdlerScheduled = false;
final long now = SystemClock.uptimeMillis();
//获取上次GC的时间
if ((BinderInternal.getLastGcTime()+MIN_TIME_BETWEEN_GCS) < now) {
//Slog.i(TAG, "**** WE DO, WE DO WANT TO GC!");
BinderInternal.forceGc("bg");
}
}
复制代码
GcIdler方法理解起来很简单、就是获取上次GC的时间,判断是否须要GC操做。若是须要则进行GC操做。这里ActivityThread中还声明了其余空闲时的任务。若是你们对其余空闲任务感兴趣,能够自行研究。
经过上文的了解,你们已经知道了Native的消息机制可能会致使主线程阻塞,那么唤醒Native消息机制**(让Native消息机制不在等待Native消息,也就是nativePollOnce()方法返回)**在整个Android的消息机制中尤其重要,这里放在这里给你们讲是由于唤醒的条件涉及到屏障消息与空闲任务。你们理解了这两个内容后再来理解唤醒的时机就相对容易一点了,这里咱们分别对唤醒的两个时机进行讲解。
boolean enqueueMessage(Message msg, long when) {
...省略部分代码
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;
} else {
//判断唤醒条件,当前消息队列头部消息是屏障消息,且当前插入的消息为异步消息
//且当前消息队列处于无消息可处理的状态
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;
}
//调用nativeWake,以触发nativePollOnce函数结束等待
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
复制代码
上述代码,咱们很明显的看见Native消息机制的唤醒,受到needWake这个变量影响,needWake ==true是在两个条件下。
Message next() {
...省略部分代码
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
...省略部分代码
//执行native层消息机制层,
//timeOutMillis参数为超时等待时间。若是为-1,则表示无限等待,直到有事件发生为止。
//若是值为0,则无需等待当即返回。该方法可能会阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
...省略部分代码
//获取空闲时处理任务的handler 用于发现线程什么时候阻塞等待更多消息的回调接口。
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
//若是消息队列中没有消息能够处理,且没有空闲任务,那么就继续等待消息
mBlocked = true;
continue;
}
...省略部分代码
}
...省略执行空闲任务代码
// 重置空闲的handler个数,由于不须要重复执行
pendingIdleHandlerCount = 0;
//当执行完空闲的handler的时候,新的native消息可能会进入,因此唤醒Native消息机制层
nextPollTimeoutMillis = 0;
}
}
复制代码
这里咱们能够看到 mBlocked = true的条件是在消息队列中没有消息能够处理,且也没有空闲任务的状况下。也就是当前mBlocked = true会影响到MessageQueue中enqueueMessage()方法是否唤醒主线程。
若是当前空闲任务完成后,**会将nextPollTimeoutMillis 置为0,**若是nextPollTimeoutMillis =0,会致使nativePollOnce直接返回,也就是会直接唤醒主线程(唤醒Native消息机制层)。
到目前为止,你们已经对整个消息的发送与取出有一个大概的了解了。这里我着重对MessageQueue取消息的流程画了一个简单的流程图。但愿你们根据对取消息有个更好的理解。
站在巨人的肩膀上。能够看得更远。该篇文章参阅了一下几本图书与源码。我这里我给了百度云盘的下载连接。你们能够按需下载。