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; mPendingIdleHandlers = 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;
参考:
https://juejin.cn/post/7055564669540368392
Github代码地址:
https://github.com/running-libo/PerformanceOpt |