上一个问题,我们提问了Handler的同步屏障机制,除此以外,还有个比较有意思的对象:
IdleHandler
1. 它有什么能力?
2. 它有什么用处?
3. 能想到一些合适的场景么。
本周2/3.
本计划这周把编辑器改成markdown的,不过忘了带电脑回家,目测需要到8月中旬了。
更多问答 >>
-
每日一问 很多时候我们说"Android16.6ms刷新一次屏幕" 正确吗?
2019-07-30 21:53 -
2019-08-01 21:44
-
每日一问 大家应该都有泛型在编译期会被擦除的概念,那么为什么我们在运行时还能读取到呢? 3/3
2019-08-04 19:44 -
每日一问 AppCompatTextView 与 TextView 1/3
2019-08-07 01:29 -
每日问答 Handler应该是大家再熟悉不过的类了,那么其中有个同步屏障机制,你了解多少呢?
2019-07-23 17:29 -
每日一问 Android 常见的制作圆角方案,有哪几种常见方式? 在Android P上什么兼容性问题
2019-07-21 16:48 -
每日一问 哪些 Context调用 startActivity 需要设置NEW_TASK,为什么?
2019-07-18 23:24 -
每日一问 如果项目要开始做编译速度优化,你会考虑从哪些地方入手?
2019-07-16 23:50 -
2019-07-14 21:09
xxxxxxxxxx
首先看下源码的解释
/**
* 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的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)方法即可
合适场景可以从以下一点或几点出发
目前可以想到的场景
1.Activity启动优化:onCreate,onStart,onResume中耗时较短但非必要的代码可以放到IdleHandler中执行,减少启动时间
2.想要在一个View绘制完成之后添加其他依赖于这个View的View,当然这个用View#post()也能实现,区别就是前者会在消息队列空闲时执行
3.发送一个返回true的IdleHandler,在里面让某个View不停闪烁,这样当用户发呆时就可以诱导用户点击这个View,这也是种很酷的操作
4.一些第三方库中有使用,比如LeakCanary,Glide中有使用到,具体可以自行去查看
赞一个!
之前回答的时候抖了个机灵 关于想到的场景第3点 这里更正一下 当IdleHandler返回true的时候,执行完后会保留这个这个事件,但这不代表消息队列为空时会不停地执行,当又有message从消息 ...查看更多
之前回答的时候抖了个机灵 关于想到的场景第3点 这里更正一下 当IdleHandler返回true的时候,执行完后会保留这个这个事件,但这不代表消息队列为空时会不停地执行,当又有message从消息队列取出之后,保留的IdleHanlder才有机会再次执行
这点很关键,关于场景3有点不适合。因为需要唤醒,才能继续这个handler
这个IdleHandler,它是声明在MessageQueue里面的一个接口,所以我们可以猜想到它跟MessageQueue一定有关系。
这个接口只有一个方法:queueIdle,字面意思就是队列空闲。
看MessageQueue的源码可以发现有两处关于IdleHandler的声明,分明是:
后面的数组,它里面放的IdleHandler实例都是临时的,也就是每次使用完(调用了queueIdle方法)之后,都会置空(mPendingIdleHandlers[i] = null)
那它们会在什么时候用到呢?
就在MessageQueue的next方法里面。
大概流程是这样的:
来看看它在源码里的使用场景:
比如在ActivityThread中,就有一个名叫GcIdler的内部类,实现了IdleHandler接口。
它在queueIdle方法被回调时,会做强行GC的操作(即调用BinderInternal的forceGc方法),但强行GC的前提是,与上一次强行GC至少相隔5秒以上。
那这个GcIdler会在什么时候使用呢?
当ActivityThread的mH(Handler)收到GC_WHEN_IDLE消息之后。
何时会收到GC_WHEN_IDLE消息?
当AMS(ActivityManagerService)中的这两个方法被调用之后:
Q:IdleHandler 有什么用?
简单来说,IdleHandler 是 Handler 中提供的一种可以在 Looper 事件循环的过程中,MessageQueue 出现空闲的时候,允许我们执行一些任务的机制。
Q:IdleHandler 如何使用?
Q:IdleHandler 的 queueIdle() 什么时候被调用?
队列出现空闲的 2 种场景:
Q:当 mIdleHandlers 不为空时,为什么不会进入死循环?
Q:IdleHandler 适合做什么?
更多细节欢迎阅读文章:《Handler的IdleHandler》
IdleHandler:空闲监听器(就像我没事做了,在群里发了个表情,这时候其他人就知道我很闲了)
在每次next获取消息进行处理时,发现莫得可以处理的消息(队列空,只有延时消息并且没到时间,同步阻塞时没有异步消息)都会通知这些订阅者。
适合做一些可有可无的东西,因为这个通知太不稳定了(就像别人说过几天请你吃饭,你要真傻等着你估计能饿死)。
看了 @bigdevil 的回答,然后去看了 MQ next() 方法的源码,是在 MQ 空闲(当 MQ 中 Message 消息全部执行完毕或在处理一个延时消息)时调用。
系统在几个地方应用了:
这个之前查 Handler 同步障碍机制的时侯遇到过,有点困惑,再去查查资料再来回答!
前两天刚在nanchen的公众号上看到有人在问这个