likes
comments
collection
share

你知道IdlerHandler吗?

作者站长头像
站长
· 阅读数 32

什么是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在消息循环中的处理过程了。

源码中的使用

你知道IdlerHandler吗? 我们可以看到调用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集合中。 你知道IdlerHandler吗?

结语

IdleHandler是Handler提供的一种在消息队列空闲时,执行任务的一种机制。它的执行时机依赖于MessageQueue的情况,如果MessageQueue一直有待执行消息需要处理,IdleHandler将得不到机会处理,这也说明IdleHandler的执行时机是不可控的,不适合执行一些优先级较高的任务。

当我们在开发中,如果遇到一些需要延迟处理的场景,可以考虑使用IdleHandler,比如在启动过程中,可以将一些优先级不高的初始化任务放到IdleHandler中,从而提升启动速度。

IdleHandler可以帮助我们实现很多功能,但是由于其不可控,使用中也会遇到很多问题,只有了解了它的原理,才能在实际工作中,更好的使用它。

转载自:https://juejin.cn/post/6971320826045923364
评论
请登录