什么是IdleHandler?
当MessageQueue为空或者目前没有需要执行的Message时会回调的接口对象。
IdleHandler 被定义在 MessageQueue 中,它看起来好像是个Handler,其实只是一个有单方法的接口,也称为函数型接口:
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();
}
在MessageQueue中有一个List存储了IdleHandler对象,当MessageQueue没有需要被执行的Message时就会遍历回调所有的IdleHandler。所以IdleHandler主要用于在消息队列空闲的时候处理一些轻量级的工作。
调用
IdleHandler的调用是在next方法中:
Message next() {
// 如果looper已经退出了,这里就返回null
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
// IdleHandler的数量
int pendingIdleHandlerCount = -1;
// 阻塞时间
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 阻塞对应时间
nativePollOnce(ptr, nextPollTimeoutMillis);
// 对MessageQueue进行加锁,保证线程安全
synchronized (this) {
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) {
// 下一个消息还没开始,等待两者的时间差
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 获得消息且现在要执行,标记MessageQueue为非阻塞
mBlocked = false;
// 一般只有异步消息才会从中间拿走消息,同步消息都是从链表头获取
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
// 没有消息,进入阻塞状态
nextPollTimeoutMillis = -1;
}
// 当调用Looper.quitSafely()时候执行完所有的消息后就会退出
if (mQuitting) {
dispose();
return null;
}
// 重点!!
// 当队列中的消息用完了或者都在等待时间延迟执行同时给pendingIdleHandlerCount<0
// 给pendingIdleHandlerCount赋值MessageQueue中IdleHandler的数量
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
// 没有需要执行的IdleHanlder直接continue
if (pendingIdleHandlerCount <= 0) {
// 执行IdleHandler,标记MessageQueue进入阻塞状态
mBlocked = true;
continue;
}
// 把List转化成数组类型
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 执行IdleHandler
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // 释放IdleHandler的引用
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
// 如果返回false,则把IdleHanlder移除
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// 最后设置pendingIdleHandlerCount为0,防止再执行一次
pendingIdleHandlerCount = 0;
// 当在执行IdleHandler的时候,可能有新的消息已经进来了
// 所以这个时候不能阻塞,要回去循环一次看一下
nextPollTimeoutMillis = 0;
}
}
代码逻辑大概如下:
- 当调用next方法的时候,会给pendingIdleHandlerCount赋值为-1
- 如果队列中没有需要处理的消息的时候,就会判断pendingIdleHandlerCount是否<0,如果是则把存储IdleHandler的list的长度
mIdleHandlers.size()
赋值给pendingIdleHandlerCount - 把list中的所有IdleHandler放到数组中。这一步是为了不让在执行IdleHandler的时候List被插入新的IdleHandler,造成逻辑混乱
- 然后遍历整个数组执行所有的IdleHandler
- 最后给pendingIdleHandlerCount赋值为0。然后再回去看一下这个期间有没有新的消息插入。因为pendingIdleHandlerCount的值为0不是-1,所以IdleHandler只会在空闲的时候执行一次
- 同时注意,如果IdleHandler返回了false,那么执行一次之后就被丢弃了。
总结
IdleHandler是一个回调接口,可以通过MessageQueue
的addIdleHandler
添加实现类。当MessageQueue中的任务暂时处理完了(没有新任务或者下一个任务延时在之后),这个时候会回调这个接口,返回false,那么就会移除它,返回true就会在下次message处理完了的时候继续回调。