在android的MessageQueue中有一个static的接口IdleHandler,这个接口用于在MessageQueue中没有可处理的Message的时候回调,这样就可以在UI线程中处理完所有的view事务之后,回调一些额外的操作而不会block UI线程(譬如 UI 线程在显示完成后,如果线程空闲我们就可以提前准备其他内容)的情况下,不过最好不要做耗时操作。
具体用法:
mHandler.getLooper().getQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
//做一些相对耗时的事情
return false;
}
})
mHandler.getLooper().getQueue()可以换成
getMainLooper().myQueue()或者Looper.myQueue()
源码说明:
/**
* 获取当前线程队列使用Looper.myQueue(),获取主线程队列可用getMainLooper().myQueue()
*/public final class MessageQueue {
......
/**
* 当前队列将进入阻塞等待消息时调用该接口回调,即队列空闲
*/ public static interface IdleHandler {
/**
* 返回true就是单次回调后不删除,下次进入空闲时继续回调该方法,false只回调单次。
*/ boolean queueIdle();
}
/**
* <p>This method is safe to call from any thread.
* 判断当前队列是不是空闲的,辅助方法
*/ public boolean isIdle() {
synchronized (this) {
final long now = SystemClock.uptimeMillis();
return mMessages == null || now < mMessages.when;
}
}
/**
* <p>This method is safe to call from any thread.
* 添加一个IdleHandler到队列,如果IdleHandler接口方法返回false则执行完会自动删除,
* 否则需要手动removeIdleHandler。
*/ public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
/**
* <p>This method is safe to call from any thread.
* 删除一个之前添加的 IdleHandler。
*/ public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
......
//Looper的prepare()方法会通过ThreadLocal准备当前线程的MessageQueue实例,
//然后在loop()方法中死循环调用当前队列的next()方法获取Message。
Message next() {
......
for (;;) {
......
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
......
//把通过addIdleHandler添加的IdleHandler转成数组存起来在mPendingIdleHandlers中
// 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.
//循环遍历所有IdleHandler
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 {
//调用IdleHandler接口的queueIdle方法并获取返回值。
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
//如果IdleHandler接口的queueIdle方法返回false说明只执行一次需要删除。
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
......
}
}
}
最后的思考:
顺着这个思路我们可以考虑优化下Activity界面的启动速度问题,由于onResume是在绘制界面之前调用,别问为什么,看源码就知道了,嘿嘿,so,把在onResume以及其之前的调用的但非必须的事件(如某些界面View的绘制)挪出来找一个时机(即绘制完成以后)去调用。那样启动时间自然就缩短了。但是整体做的事并没有明显变化。那么这个时机是什么呢?
从上边的源码可知道,IdleHandler即在looper里面的message处理完了的时候去调用,这不就是我们onResume调用完了以后的时机么。明显的IdleHandler在onResume以及performTraversals绘制之后调用。由这个思路我把自己负责的页面中的一些界面的绘制逻辑挪到了IdleHandler中,比如自定义view的绘制加载,适配器的绑定以及其他pop或者dialog的初始化等,这会大大减少打开界面的时间,又不影响使用。
好了 动动可爱的手指行动起来优化代码吧!
喜欢 就关注吧,欢迎投稿、交流!
如有任何疑问可在文章底部留言。为了防止恶意评论,本博客现已开启留言审核功能。但是博主会在后台第一时间看到您的留言,并会在第一时间对您的留言进行回复!欢迎交流!
本文链接: https://leetcode.jp/idlehandler你用对地方了吗?/