背景
我们的在业务开发的过程中,可能会遇到这样子的情况,需要再UI线程
空闲的时候,做一些操作,那应该怎样子实现呢?
MessageQueue.IdleHandler
MessageQueue
给我们提供了一个IdleHandler
的接口,其定义如下:
/**
* 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();
}
根据源码的注释我们可以知道,这是一个在消息队列的全部消息处理完成或者没有消息需要处理在阻塞的过程中等待新消息的时候调用的,其返回值决定了,是否需要保持这个监听。具体实现方式如下:
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Looper.myQueue().addIdleHandler(mKeepIdleHandler);
Looper.myQueue().addIdleHandler(mOnlyOnceIdleHandler);
findViewById(R.id.textView).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG, " 按钮被点击了 ");
}
});
}
private MessageQueue.IdleHandler mKeepIdleHandler = new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
Log.i(TAG, " mKeepIdleHandler queueIdle 被回调了 ");
return true;
}
};
private MessageQueue.IdleHandler mOnlyOnceIdleHandler = new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
Log.i(TAG, "mOnlyOnceIdleHandler queueIdle 被回调了 ");
return false;
}
};
实现自己的IdleHandler,并在queueIdle
接口中返回true
表明需要保持监听。在我们的demo运行起来之后,会看到LogCat
有对应的输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yR8vYgJA-1575798965736)(media/15757986947143.jpg)]
可以看到,返回true
的场景,会再之后UI线程空闲的时候,再次回调,但如果是返回false
的场景,只会执行调用一次。
另外需要注意,这里的回调,并不是说,当前UI线程是空闲状态,就会一直不断回调,如果没有更多的消息输入,回调一次之后,需要等到再次有消息输入并处理完成之后,才会有另外一次的调用。具体原理可以从源码中看到。
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// 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.
//首次遍历,原始值是-1,故进入
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 MessageQueue.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 MessageQueue.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;
设置pendingIdleHandlerCount
的默认值为-1,这个会在判断是否还有需要通知的IdleHandler的时候有用。如果是初始状态,会获取一次待通知的IdleHandler
列表。如果存在需要通知的IdleHandler
,会逐一编译进行通知,并且根据keep = idler.queueIdle();
的返回值,来决定是否需要移除监听。