程序员的资源宝库

网站首页 > gitee 正文

IdleHandler的使用及原理(idle是干什么用的)

sanyeah 2024-04-01 11:14:13 gitee 3 ℃ 0 评论

IdleHandler的使用及原理 - 简书 (jianshu.com)
 

IdleHandler方式就是利用其特性,只有CPU空闲的时候才会执行相关任务,并且我们可以分批进行任务初始化,可以有效缓解界面的卡顿。

简单用法代码如下:

        Looper.myQueue().addIdleHandler(object: MessageQueue.IdleHandler {
            override fun queueIdle(): Boolean {
                //执行任务
                return false;
            }
        })

可以将上述代码添加到Activity onCreate中,在queueIdle()方法中实现延迟执行任务,在主线程空闲,也就是activity创建完成之后,它会执行queueIdle()方法中的代码。

如何设置是否重复执行

queueIdle()返回true表示可以反复执行该方法,即执行后还可以再次执行;返回false表示执行完该方法后会移除该IdleHandler,即只执行一次。

IdleHandler源码解析:

IdleHandler属于MessageQueue内部接口,只有一个queueIdle()方法声明。通过方法addIdleHandler将我们的idleHandler添加到集合中。

public final class MessageQueue {

    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
  
 
     /**
     * Add a new {@link IdleHandler} to this message queue.  This may be
     * removed automatically for you by returning false from
     * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
     * invoked, or explicitly removing it with {@link #removeIdleHandler}.
     *
     * <p>This method is safe to call from any thread.
     *
     * @param handler The IdleHandler to be added.
     */
    public void addIdleHandler(@NonNull IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }
 
 
    /**
     * Callback interface for discovering when a thread is going to block
     * waiting for more messages.
     */
    public static interface IdleHandler {
        boolean queueIdle();
    }
}
IdleHandler的queueIdle方法何时执行

在MessageQueue取消息的next方法中,IdleHandler相关代码如下:

    @UnsupportedAppUsage
    Message next() {
        ...
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                ...
                if (msg != null) {
                 ...

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                   //如果消息队列为空或者消息执行时间还未到,则获取IdleHandler队列的大小,下面需要用到
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                   //无需要执行的  idle handler,则继续阻塞
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                //将IdleHandler列表转为数组
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            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();   //开始顺序执行所有IdleHandler的queueIdle方法
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {   //如果发现有queueIdle()方法返回false,则线程安全地删除这个idlehandler不再执行queueIdle
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
            ...
        }
    }
多任务延迟初始化实战:

我们根据queueIdle返回true时可以执行多次的特点,可以实现一个任务列表,然后从这个任务列表中取任务执行。

public class TaskDispatcher {

    private Queue<Runnable> delayTasks = new LinkedList<>();

    private MessageQueue.IdleHandler idleHandler = () -> {
        if (delayTasks.size() > 0) {
            Runnable task = delayTasks.poll();
            if (task != null) {
                task.run();
            }
        }
        return !delayTasks.isEmpty();  //只要task任务不为空,就继续执行初始化
    };

    public TaskDispatcher addTask(Runnable task) {
        delayTasks.add(task);
        return this;
    }

    public void start() {
        Looper.myQueue().addIdleHandler(idleHandler);
    }
}

创建一个ARouter初始化和Webview初始的task

public class WebviewInitTask implements Runnable {

    @Override
    public void run() {
        Log.i("minfo", "初始化Okhttp");
    }
}

public class WebviewInitTask implements Runnable {

    @Override
    public void run() {
        Log.i("minfo", "初始化Webview");
    }
}

界面显示后进行调用:

override fun onCreate(savedInstanceState: Bundle?) {
        setTheme(R.style.AppTheme)
        super.onCreate(savedInstanceState)
        
            TaskDispatcher()
            .addTask(ARouterInitTask())
            .addTask(WebviewInitTask())
            .start()
    }

打印执行结果

 
关于IdleHandler问题

Q:IdleHandler 有什么用?

IdleHandler 是 Handler 提供的一种在消息队列空闲时,执行任务的时机;
当 MessageQueue 当前没有立即需要处理的消息时,会执行 IdleHandler;

Q:MessageQueue 提供了 add/remove IdleHandler 的方法,是否需要成对使用?

不是必须;
IdleHandler.queueIdle() 的返回值,可以移除加入 MessageQueue 的 IdleHandler;
Q:当 mIdleHanders 一直不为空时,为什么不会进入死循环?

只有在 pendingIdleHandlerCount 为 -1 时,才会尝试执行 mIdleHander;
pendingIdlehanderCount 在 next() 中初始时为 -1,执行一遍后被置为 0,所以不会重复执行;
Q:是否可以将一些不重要的启动服务,搬移到 IdleHandler 中去处理?

不建议;
IdleHandler 的处理时机不可控,如果 MessageQueue 一直有待处理的消息,那么 IdleHander 的执行时机会很靠后;
Q:IdleHandler 的 queueIdle() 运行在那个线程?

陷进问题,queueIdle() 运行的线程,只和当前 MessageQueue 的 Looper 所在的线程有关;
子线程一样可以构造 Looper,并添加 IdleHandler;

参考:

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表