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

    **目前可以想到的场景:**
    1.`Activity`启动优化:`onCreate,onStart,onResume `中耗时较短但非必要的代码可以放到`IdleHandler`中执行,减少启动时间
    2.想要一个View绘制完成之后添加其他依赖于这个`View`的`View`,当然这个`View#post()`也能实现,区别就是前者会在消息队列空闲时执行。
    3.发生一个返回`true`的`IdleHandler`,在里面让某个`View`不停闪烁,这样当用户发呆时就可以诱导用户点击这个`View`,这也是种很酷的操作。
    4.一些第三方库中使用,比如[LeakCanary](https://github.com/square/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`消息全部执行完毕或者处理一个延时消息)时调用。

    **系统还在这几个地方应用了:**
    1. `Activity onResume` 以及`view`初始化完成之后调用
    2. 强行`GC`,当内存不足时调用
    3. `TTS`合成之后发生广播
    4. 在`Activity onCreate()`执行前添加
    5. 在键盘相关调用

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

    参考链接:
    [每日一问 听说过Handler中的IdleHandler吗?](https://www.wanandroid.com/wenda/show/8723)

  • 相关阅读:
    Android进阶篇系统缓存(一)
    Android提高篇自定义Menu(二)
    Android进阶篇Canvas Bitmap
    Android进阶篇分析伪3D旋转
    Android进阶篇捕获app错误信息
    Android进阶篇Service和广播
    Android进阶篇清除WebView的缓存
    Android进阶篇Parcelable包裹类
    Android进阶篇GestureDetector初探
    Android进阶篇下拉刷新
  • 原文地址:https://www.cnblogs.com/merbn/p/11246118.html
Copyright © 2020-2023  润新知