文章目录
- 一、MessageQueue 空闲任务 IdleHandler 机制
- 二、MessageQueue 中空闲任务 IdleHandler 相关源码
一、MessageQueue 空闲任务 IdleHandler 机制
在 消息队列 MessageQueue 的 next 获取下一条消息的方法中 ,
在循环获取消息的最后有如下代码 :
代码语言:javascript复制public final class MessageQueue {
@UnsupportedAppUsage
Message next() {
// ...
// 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)) {
// 如果取出的 消息为空 , 就会执行 IdleHandler
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);
// ...
}
}
开始判定 pendingIdleHandlerCount 大小 , 如果小于
则执行后续 与 IdleHandler 相关操作 ;
IdleHandler 是一个 接口 , 代码如下 :
代码语言:javascript复制public final class MessageQueue {
/**
* 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();
}
}
在上述如果取出的 mMessage 消息为空 mMessages == null , 则执行 IdleHandler ;
IdleHandler 使用时 , 调用 addIdleHandler 函数 , 该函数会将 IdleHandler 加入到 mIdleHandlers 集合中 ,
MessageQueue 消息队列 addIdleHandler 函数如下 :
代码语言:javascript复制public final class MessageQueue {
/**
* Add a new {@link IdleHandler} to this message queue. This may be
* removed automatically for you by returning false from
* {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
* invoked, or explicitly removing it with {@link #removeIdleHandler}.
*
* <p>This method is safe to call from any thread.
*
* @param handler The IdleHandler to be added.
*/
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
}
在 MessageQueue 的 next 方法中继续向下执行 , 开始遍历 mIdleHandlers 集合 , 先将集合 mIdleHandlers 转为 mPendingIdleHandlers 数组 , 然后遍历该数组 ,
遍历时 , 调用 IdleHandler 对象的 queueIdle 方法 ,
如果上面的 queueIdle 方法返回值是 false , 就会从 mIdleHandlers 集合中 移除该元素 ,
如果上面的 queueIdle 方法返回值是 true , 那么下一次再次执行到这里时 , 还会 继续回调该函数 ;
上述操作对应的 MessageQueue 的 next 方法内的部分源码 :
代码语言:javascript复制public final class MessageQueue {
@UnsupportedAppUsage
Message next() {
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;
}
}
IdleHandler 的作用是 , 当消息队列 MessageQueue 中没有消息处理时 , 处于闲置状态时 , 此时就会回调一次用户注册的 IdleHandler ,
如果该 IdleHandler 的 queueIdle 方法返回 false , 那么该 IdleHandler 只会 执行一次 ,
如果该 IdleHandler 的 queueIdle 方法返回 true , 则 每次空闲 , 都要执行一次该 IdleHandler ;
二、MessageQueue 中空闲任务 IdleHandler 相关源码
代码语言:javascript复制public final class MessageQueue {
@UnsupportedAppUsage
Message mMessages;
@UnsupportedAppUsage
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
private IdleHandler[] mPendingIdleHandlers;
@UnsupportedAppUsage
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)) {
// 如果取出的 消息为空 , 就会执行 IdleHandler
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;
}
}
/**
* 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();
}
}