源码实现
MessageQueue中next()方法关于IdleHandler的逻辑:
源码
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
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();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
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());
}
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 {
// Got a message.
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 {
// No more messages.
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.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
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);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
整体作用回顾:MessageQueue.next()
该方法被 Looper.loop() 持续调用,用于:
- 从 MessageQueue 中取出下一个需要处理的 Message;
- 如果暂时没有消息可处理,就会等待,并可能调用 IdleHandler;
- 如果调用了 Looper.quit() 或 quitSafely(),就会返回 null 终止循环。
IdleHandler 的触发时机
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
满足两个条件时会触发 IdleHandler:
- 当前没有消息(mMessages == null),即队列空了;
- 或者有消息,但 该消息的执行时间还没到(now < mMessages.when)。
这说明:只在队列“暂时空闲”时,IdleHandler 才会被调度执行。
IdleHandler 的执行逻辑
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
...
boolean keep = idler.queueIdle();
...
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
每个 IdleHandler 会被执行:
- 如果 queueIdle() 返回 true:会保留在 mIdleHandlers 中,下次继续执行;
- 返回 false:移除,不再执行。
执行完所有 idle handlers 后,重置 pendingIdleHandlerCount = 0,然后继续 loop。
完整 IdleHandler 生命周期流程
- 调用 Looper.myQueue().addIdleHandler(…) 注册;
- 当队列空闲(无消息或下一条未到时);
- queueIdle() 被调用;
- 若返回 false,会从 mIdleHandlers 中移除;
- 如果队列重新被填充,IdleHandler 不会触发;
- 下一次空闲,仍存在的 IdleHandler 会再被执行。
为啥只在首次遍历时调用?
// We only ever reach this code block during the first iteration.
因为:
- IdleHandler 被认为是“空闲时的补充执行”,一旦有消息入队,就优先处理消息。
- 如果你在 queueIdle() 中再次入队消息,那么下一轮 next() 时将不会再触发 IdleHandler,逻辑会重新回到主循环,优先处理消息。
IdleHandler 是线程安全的吗?
基本安全,但:
- 添加 (addIdleHandler) 和删除 (removeIdleHandler) 发生在 MessageQueue 的同步块中;
- 所以推荐只在当前 Looper 所在线程操作,避免并发添加/移除导致不一致。
总结 IdleHandler 的执行逻辑
步骤 | 条件 |
---|---|
1. 是否进入 IdleHandler | mMessages == null 或 now < mMessages.when |
2. 是否调用 queueIdle() | 是(每个已注册 IdleHandler 都调用) |
3. queueIdle() 返回值 | true → 保留,false → 删除 |
4. 执行顺序 | 所有注册的 IdleHandler |
5. 中断条件 | 有消息入队、或者 quit() |
真实使用建议
- 尽量轻量:不要在 queueIdle() 中执行复杂耗时任务,否则会阻塞主线程。
- 不要依赖它一定执行,消息队列活跃时可能完全不触发。
- 适合用于延迟初始化、预加载等“有最好,没有也行”的任务。