[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();    }

注释中明确的指出当消息队列空闲时会执行IdelHandlerqueueIdle()方法,该方法返回一个boolean值, 如果为false则执行完毕之后移除这条消息, 如果为true则保留,等到下次空闲时会再次执行,

下面看下MessageQueuenext()方法可以发现确实是这样。

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带来不同结果

目前可以想到的场景:

  1. Activity启动优化:onCreate,onStart,onResume中耗时较短但非必要的代码可以放到IdleHandler中执行,减少启动时间
  2. 想要一个View绘制完成之后添加其他依赖于这个ViewView,当然这个View#post()也能实现,区别就是前者会在消息队列空闲时执行。
  3. 发生一个返回trueIdleHandler,在里面让某个View不停闪烁,这样当用户发呆时就可以诱导用户点击这个View,这也是种很酷的操作。
  4. 一些第三方库中使用,比如LeakCanary,Glide中使用到,具体可以自行去查看。

Answer2:

这个IdleHandler,它是声明在MessageQueue里面的一个接口,所以我们可以猜想到它跟MessageQueue一定有关系,这个接口只有一个方法:queueIdle,字面意思就是队列空闲。 看MessageQueue的源码可以发现有两处关于IdleHandler的声明,分别是:

  • 存放IdleHandlerArrayList(mIdleHandlers)
  • 还有一个IdleHandler数组(mPendingIdleHandlers)

后面的数组,它里面放的IdleHandler实例都是临时的,也就是每次使用完(调用了queueIdle方法)之后,都会置空(mPendingIdleHandlers[i]=null) 那它们会在什么时候用到呢? 就是在MessageQueuenext方法里面
大概流程是这样的:
1.如果本次循环拿到的Message为空,或者!这个Message是一个延时的消息而且还没到指定触发时间,那么就认定当前的队列为空闲时间。
2.接着会遍历mPendingIdleHandlers数组(这个数组里面的元素每次都会到mIdleHandlers中去拿)来调用每一个IdleHandler实例的queueIdle方法。
3.如果这个方法返回false的话,那么这个实例就会从mIdleHandlers中移除,也就是当下次队列空闲的时候,不会继续回调它的queueIdle方法了。

来看看它在源码里的使用场景:
比如在ActivityThread中,就有一个名叫GcIdler的内部类,实现了IdleHandler接口。 它在queueIdle方法被回调时,会做强行GC的操作(即调用BinderInternalfaceGc方法),但强行GC的前提是与上一次强行GC至少相隔5秒以上。

那这个GcIdler会在什么时候使用呢?
ActivityThreadmH(Handler)收到GC_WHEN_IDLE消息之后。

何时会收到GC_WHEN_IDLE消息?
AMS(ActivityManagerService)中的这两个方法被调用之后:

  • doLowMemReportIfNeededLocked,这个方法看名字就知道是不够内存的时候调用了。
  • activityIdle,这个方法呢,就是当ActivityThreadhandleResumeActivity方法被调用时(ActivityonResume方法也是在这方法里面回调)调用的。

Answer3:

IdleHandler:空闲监听器(就好像我没事做了,在群里发了个表情,这时候其他人就知道我很闲了) 在每次next获取消息进行处理时,发现没得可以处理的消息(队列空,只有延时消息并且没到时间,同步阻塞时没有异步消息)都会通知这些订阅者。

适合做一些可有可无的东西,因为这个通知太不稳定了(就像别人说过几天请你吃饭,你要真傻等着估计能饿死)

Answer4:

去看了MessageQueuenext方法的源码,是在MessageQueue空闲(当MQ中Message消息全部执行完毕或者处理一个延时消息)时调用。

系统还在这几个地方应用了:

  1. Activity onResume 以及view初始化完成之后调用
  2. 强行GC,当内存不足时调用
  3. TTS合成之后发生广播
  4. Activity onCreate()执行前添加
  5. 在键盘相关调用

结合系统的使用场景,推测一下,可以做UI操作,不能太耗时,调用的时机比较模糊关联性不能太强。

最后

如果你看到了这里,觉得文章写得不错就给个呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。

希望读到这的您能转发分享关注一下我,以后还会更新技术干货,谢谢您的支持!

加入Android开发交流群(820198451)还可以免费获取更多架构资料和面试专题资料

Android架构师之路很漫长,一起共勉吧!


更多相关文章

  1. android 关闭app简要说明
  2. android获取手机屏幕大小
  3. Android(安卓)Google map使用
  4. Android(安卓)简单定制OptionMenu外观
  5. Cocos2d-x android使用onKeyDown监听返回键实现二次返回退出
  6. setResult(Activity.RESULT_OK)失败分析
  7. Android(安卓)得到当前已连接的wifi的信号强度
  8. Android(安卓)编程下 Touch 事件的分发和消费机制

随机推荐

  1. 使用mongovue把sqlserver数据导入mongodb
  2. sql server自动生成拼音首字母的函数
  3. sqlserver实现oracle的sequence方法
  4. sql中count或sum为条件的查询示例(sql查
  5. mssql使用存储过程破解sa密码
  6. 没有sa密码无法集成windows身份验证登录
  7. 参考sql2012存储过程写的统计所有用户表
  8. sql server 2000阻塞和死锁问题的查看与
  9. sql 2000 无法执行查询,因为一些文件缺少
  10. sql server 2000管理单元初始化失败的解