「这是我参与11月更文挑战的第18天,活动详情查看:2021最后一次更文挑战」
Handler机制原理
3.MessageQueue
上篇文章介绍了enqueueMessage,这个方法功能是将消息插入到消息队列中。还有next方法和quit方法在本篇文章进行讲解。
next 方法从消息队列中取出一条消息并将其从消息队列中移除。
Message next() {
//...
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞方法Ptr是MessageQueue的一个long型成员变量,关联的是一个在C++层的MessageQueue,阻塞操作就是通过底层的这个MessageQueue来操作的;当队列被放弃的时候其变为0。
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
//msg.target == null表示此消息为消息屏障(通过postSyncBarrier方法发送来的)
//如果发现了一个消息屏障,会循环找出第一个异步消息(如果有异步消息的话),所有同步消息都将忽略(平常发送的一般都是同步消息)
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
//如果消息还没有到达时间,会设置阻塞时间nextPollTimeoutMillis,在下次循环开始时会调用nativePollOnce进行消息阻塞。
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//取出消息设置mBlocked = false代表目前没有被阻塞。
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;
}
if (mQuitting) {
dispose();
return null;
}
//...处理IdleHandler任务
}
}
nativePollOnce阻塞方法,主要是通过native层的epoll监听文件描述符的写入事件来实现的,nextPollTimeoutMillis阻塞时间会有以下三种情况:
- 如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
- 如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
- 如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
总结一下,当首次进入或消息已经被处理后,mMessages为null也就是队列中没有消息。nextPollTimeoutMillis=-1,回去处理IdleHandler任务,IdleHandler ,就是 Handler 机制提供的一种,可以在 Looper 事件循环的过程中,当出现空闲的时候,允许我们执行任务的一种机制。
如果发现消息屏障回调过后面的同步消息。
如果消息没有到时,则更新nextPollTimeoutMillis,进行阻塞.
如果到时,则直接返回此消息交由Looper去处理。