1.使用方法及场景
之前做过冷启动优化,在冷启动的场景有很多的任务其实并不需要马上启动,通常的做法就是做一个延迟启动,如下所示
Handler mHandler = new Handler();
mHandler.postDelayed(() -> {
//do something
}, 1000);
将任务延迟启动1000ms,但是这个延迟启动的时间不好确定,只能是自己预估的,对于一些高端手机1000ms可能多了,一些低端手机可能1000ms还不够。这个时候IdelHandler就可以解决这个问题,它能够在CPU空闲的时候再执行指定的任务。
使用方法也很简单,如下所示,调用addIdleHandler方法就可以了
MessageQueue.IdleHandler idleHandler = new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
//do something
return false;
}
};
Looper.getMainLooper().getQueue().addIdleHandler(idleHandler);
2.基本原理
我们来看下IdelHandler到底是怎么实现的。
首先来看下这个接口,如注释所示,返回true和返回false是有区别的。
public static interface IdleHandler {
//返回true,表示这个接口一直有效,下次cpu空闲时还会调用
//返回false,表示这个接口只会被调用一次
boolean queueIdle();
}
而addIdleHandler会将IdelHandler插入到mIdleHandlers里面,mIdleHandlers是一个ArrayList
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函数,这里是MessageQueue拿到下一条消息的逻辑。
next函数是一个死循环,在注释1处是一个阻塞函数,能够解除阻塞并拿到返回值只有三种情况,出错,超时,有消息产生。
注释2处省略了代码,就是从链表当中取出普通消息,如果有则直接返回。
从注释3处就开始处理IdelHandler,我们可以看到pendingIdleHandlerCount表示idelHandlers的个数,初始值为-1,当mMessage为空即当前无消息需要处理,或者当前的时间小于消息的时间即当前的消息还不需要处理,然后pendingIdleHandlerCount才真正获取idelHandlers的size。
注释4处表示,返回值为false时,就直接从列表当中移除,当下次cpu空闲时,就不再触发这个IdelHandler了。
Message next() {
......
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//1.阻塞函数
nativePollOnce(ptr, nextPollTimeoutMillis);
//2.从链表里面取出消息
......
//3.开始处理IdelHandler
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);
}
//4.如果keep为false,则就从列表当中去除
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;
}
}
3.总结
其实IdelHandler的原理挺简单的,我们可以根据它的特性,需要懒加载的场景可以使用。
另外在framework当中有些场景也使用到了,如GC的场景,只有当cpu空闲的时候才会去gc
void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
Looper.myQueue().addIdleHandler(mGcIdler);
}
mH.removeMessages(H.GC_WHEN_IDLE);
}
final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
return false;
}
}