[Android] Handler中的IdleHandler
抛出
Handler
中的IdleHandler
- 它有什么能力?
- 它有什么用处?
- 能想到一些合适的场景吗?
Answer1:
首先看下源码的注释
/**
* 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();
}
注释中明确的指出当消息队列空闲时会执行IdelHandler
的queueIdle()
方法,该方法返回一个boolean
值, 如果为false
则执行完毕之后移除这条消息, 如果为true
则保留,等到下次空闲时会再次执行,
下面看下MessageQueue
的next()
方法可以发现确实是这样。
Message next() {
......
for (;;) {
......
synchronized (this) {
// 此处为正常消息队列的处理
......
if (mQuitting) {
dispose();
return null;
}
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);
}
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);
}
}
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
处理完IdleHandler
后会将nextPollTimeoutMillis
设置为0
,也就是不阻塞消息队列, 当然要注意这里执行的代码同样不能太耗时,因为它是同步执行的,如果太耗时肯定会影响后面的message
执行。 能力大概就是上面讲的这样,那么能力决定用处,用处从本质上讲就是趁着消息队列空闲的时候干点事情,当然具体用处还是要看具体的处理。 要使用IdleHandler
只需要调用MessageQueue#addIdleHandler(IdleHandler handler)
方法即可
合适场景可以从下面几点出发:
- 消息队列相关
- 主线程能干的事情
- 返回true和false带来不同结果
目前可以想到的场景:
-
Activity
启动优化:onCreate,onStart,onResume
中耗时较短但非必要的代码可以放到IdleHandler
中执行,减少启动时间 - 想要一个View绘制完成之后添加其他依赖于这个
View
的View
,当然这个View#post()
也能实现,区别就是前者会在消息队列空闲时执行。 - 发生一个返回
true
的IdleHandler
,在里面让某个View
不停闪烁,这样当用户发呆时就可以诱导用户点击这个View
,这也是种很酷的操作。 - 一些第三方库中使用,比如LeakCanary,Glide中使用到,具体可以自行去查看。
Answer2:
这个IdleHandler
,它是声明在MessageQueue
里面的一个接口,所以我们可以猜想到它跟MessageQueue
一定有关系,这个接口只有一个方法:queueIdle
,字面意思就是队列空闲。 看MessageQueue
的源码可以发现有两处关于IdleHandler
的声明,分别是:
- 存放
IdleHandler
的ArrayList(mIdleHandlers)
- 还有一个
IdleHandler
数组(mPendingIdleHandlers)
后面的数组,它里面放的IdleHandler
实例都是临时的,也就是每次使用完(调用了queueIdle
方法)之后,都会置空(mPendingIdleHandlers[i]=null)
那它们会在什么时候用到呢? 就是在MessageQueue
的next
方法里面
大概流程是这样的:
1.如果本次循环拿到的Message
为空,或者!这个Message
是一个延时的消息而且还没到指定触发时间,那么就认定当前的队列为空闲时间。
2.接着会遍历mPendingIdleHandlers
数组(这个数组里面的元素每次都会到mIdleHandlers中去拿)来调用每一个IdleHandler
实例的queueIdle
方法。
3.如果这个方法返回false
的话,那么这个实例就会从mIdleHandlers
中移除,也就是当下次队列空闲的时候,不会继续回调它的queueIdle方法了。
来看看它在源码里的使用场景:
比如在ActivityThread
中,就有一个名叫GcIdler
的内部类,实现了IdleHandler
接口。 它在queueIdle
方法被回调时,会做强行GC
的操作(即调用BinderInternal
的faceGc
方法),但强行GC
的前提是与上一次强行GC
至少相隔5秒以上。
那这个GcIdler会在什么时候使用呢?
当ActivityThread
的mH
(Handler)收到GC_WHEN_IDLE
消息之后。
何时会收到GC_WHEN_IDLE
消息?
当AMS
(ActivityManagerService)中的这两个方法被调用之后:
-
doLowMemReportIfNeededLocked
,这个方法看名字就知道是不够内存的时候调用了。 -
activityIdle
,这个方法呢,就是当ActivityThread
的handleResumeActivity
方法被调用时(Activity
的onResume
方法也是在这方法里面回调)调用的。
Answer3:
IdleHandler
:空闲监听器(就好像我没事做了,在群里发了个表情,这时候其他人就知道我很闲了) 在每次next获取消息进行处理时,发现没得可以处理的消息(队列空,只有延时消息并且没到时间,同步阻塞时没有异步消息)都会通知这些订阅者。
适合做一些可有可无的东西,因为这个通知太不稳定了(就像别人说过几天请你吃饭,你要真傻等着估计能饿死)
Answer4:
去看了MessageQueue
的next
方法的源码,是在MessageQueue
空闲(当MQ中Message
消息全部执行完毕或者处理一个延时消息)时调用。
系统还在这几个地方应用了:
-
Activity onResume
以及view
初始化完成之后调用 - 强行
GC
,当内存不足时调用 -
TTS
合成之后发生广播 - 在
Activity onCreate()
执行前添加 - 在键盘相关调用
结合系统的使用场景,推测一下,可以做UI操作,不能太耗时,调用的时机比较模糊关联性不能太强。
最后
如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。
希望读到这的您能转发分享和关注一下我,以后还会更新技术干货,谢谢您的支持!
加入Android开发交流群(820198451)还可以免费获取更多架构资料和面试专题资料
Android架构师之路很漫长,一起共勉吧!