你知道IdlerHandler吗?
什么是IdleHandler(源码分析)
IdleHandler是Handler机制提供的一种可以在Looper循环的过程中,当出现空闲的时候,允许我们执行其他任务的一种机制。 看一下它在源码中的体现:
/**
* 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();
}
IdleHandler是一个接口,定义在MessageQueue中,实现类需要实现queueIdle方法,同时这个方法有一个boolean类型的返回值。在介绍这个返回值有什么用处之前,我们先看看这样几个跟IdleHandler有关的变量:
同样是在MessageQueue这个类中:
//存放IdleHandler的集合
@UnsupportedAppUsage
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
//IdleHandler类型的数组
private IdleHandler[] mPendingIdleHandlers;
还是这个类,再看几个跟IdleHandler有关的方法:
//注册一个IdleHandler
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
//注销一个IdleHandler
public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
在addIdleHandler方法中,如果idleHandler为空则抛出异常,否则就将该idleHandler加入到之前定义的集合中。这里我们知道了,原来我们定义的IdleHandler是要加入到一个集合中的。那么该如何利用这个集合来搞事情呢?既然IdleHandler是handler范围内的,那么处理消息就应该还是Looper的那套机制吧,我们去看一下。
首先looper会调用loop()方法开启循环
public static void loop() {
final Looper me = myLooper();
...
for (;;) {
//调用MessageQueue的next方法
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
loop方法中会调用MessageQueue的next方法,又回到了MessageQueue中,研究一下next方法。
Message next() {
//用来保存注册到消息队列中的IdleHandler的个数
int pendingIdleHandlerCount = -1; // -1 only during first iteration
for (;;) {
//JNI方法,如果没有消息处理 则进入休眠状态
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
...
//执行到这里说明线程要进入睡眠状态了
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)];
}
//将集合中的IdleHandler拷贝到之前定义的数组中
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
//遍历数组,并依次调用数组中IdleHandler的queueIdle方法执行
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 {
//拿到queueIdle方法的返回值
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
//如果queueIdle方法返回false,则进入这个判断
if (!keep) {
//从集合中移除处理过的IdleHandler,下次执行next方法时就没有这个消息了
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
//将pendingIdleHandlerCount从新设置为0 保证在一次next的执行过程中,只处理一次IdleHandler数据
pendingIdleHandlerCount = 0;
...
}
}
一个线程在消息循环中,当一个线程的消息队列为空,或者保存在消息队列头部的消息的处理时间大于系统的当前时间时,线程就会无事可做。这时候线程处于一种空闲状态,接下来就会进入睡眠等待状态。不过在进入睡眠等待之前,这个线程会处理已经注册了的空闲消息。
-
之前定义的集合和数组也排上了用场,首先将idleHandler注册到集合mIdleHandlers中,在遍历idleHandler的时候会先将集合中的数据拷贝到数组mPendingIdleHandlers中,再进行遍历处理。
-
queueIdle方法的返回值决定了这个IdleHandler是否会从mIdleHandlers集合中移除。也就是说,当 queueIdle() 方法返回 true时会多次执行,即 IdleHandler 执行一次后不会从 IdleHandler 的队列中删除,等下次空闲时间到来时还会继续执行。返回false只会执行一次
通过以上的分析,就明白了IdleHandler在消息循环中的处理过程了。
源码中的使用
我们可以看到调用addIdleHandler注册IdleHandler的地方有这么多,我们看看ActivityThread中的IdleHandler
GcIdler、PurgeIdler
//定义了GCIdleHandler
final GcIdler mGcIdler = new GcIdler();
//定义资源处理IdleHandler
final PurgeIdler mPurgeIdler = new PurgeIdler();
...
final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
purgePendingResources();
return false;
}
}
final class PurgeIdler implements MessageQueue.IdleHandler {
@Override
public boolean queueIdle() {
purgePendingResources();
return false;
}
}
...
//注册
@UnsupportedAppUsage
void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
Looper.myQueue().addIdleHandler(mGcIdler);
}
mH.removeMessages(H.GC_WHEN_IDLE);
}
//注册
void schedulePurgeIdler() {
if (!mPurgeIdlerScheduled) {
mPurgeIdlerScheduled = true;
Looper.myQueue().addIdleHandler(mPurgeIdler);
}
mH.removeMessages(H.PURGE_RESOURCES);
}
...
以mGcIdler为例,在ActivityThread的H类中接收到GC_WHEN_IDLE消息后,就会执行scheduleGcIdler进行添加IdleHandler,注册到IdleHandler集合中。
结语
IdleHandler是Handler提供的一种在消息队列空闲时,执行任务的一种机制。它的执行时机依赖于MessageQueue的情况,如果MessageQueue一直有待执行消息需要处理,IdleHandler将得不到机会处理,这也说明IdleHandler的执行时机是不可控的,不适合执行一些优先级较高的任务。
当我们在开发中,如果遇到一些需要延迟处理的场景,可以考虑使用IdleHandler,比如在启动过程中,可以将一些优先级不高的初始化任务放到IdleHandler中,从而提升启动速度。
IdleHandler可以帮助我们实现很多功能,但是由于其不可控,使用中也会遇到很多问题,只有了解了它的原理,才能在实际工作中,更好的使用它。
转载自:https://juejin.cn/post/6971320826045923364