美文网首页
Android延迟加载方案之IdleHandler

Android延迟加载方案之IdleHandler

作者: 队长只有一个 | 来源:发表于2020-06-21 18:29 被阅读0次

一、背景

我们在做启动性能优化的时候,需要尽可能多地减少启动阶段主线程执行的任务时长。对一些非启动阶段一定需要完成的任务,我们可以把他放到应用启动完成之后去执行,这就是启动性能优化中的延迟加载方案。

二、常规方案

一般的方案是通过handler.postDelay延迟一段时间执行。但这种方案延迟的时间不好把握,配置高的机器和配置低的机器时间也不一样。而且如果延迟执行的任务较多, 且需要在主线程中执行,则在执行延迟任务时会因为一次性执行多个任务而导致主线程被占用一段时间造成用户操作卡顿。

三、更优方案

IdleHandler能在当前线程消息队列空闲时执行一些事情,且可以一个个任务单独执行,不用一次性执行所有任务,缓解主线程卡顿现象。使用方法如下:

//getMainLooper().myQueue()或者Looper.myQueue()
Looper.myQueue().addIdleHandler(new IdleHandler() {  
    @Override  
    public boolean queueIdle() {  
        //你要处理的事情
        return false;    
    }  
});

queueIdle返回false表示只执行一次,如果返回true,则下次在消息队列空闲的时候还会执行,这样就可以实现一个任务一个任务的执行,不会一次性占用当前线程过多时间而造成卡顿。

四、IdleHandler源码分析

/**
 * 获取当前线程队列使用Looper.myQueue(),获取主线程队列可用getMainLooper().myQueue()
 */
public final class MessageQueue {
    ......
    /**
     * 当前队列将进入阻塞等待消息时调用该接口回调,即队列空闲
     */
    public static interface IdleHandler {
        /**
         * 返回true就是单次回调后不删除,下次进入空闲时继续回调该方法,false只回调单次。
         */
        boolean queueIdle();
    }

    /**
     * <p>This method is safe to call from any thread.
     * 判断当前队列是不是空闲的,辅助方法
     */
    public boolean isIdle() {
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            return mMessages == null || now < mMessages.when;
        }
    }

    /**
     * <p>This method is safe to call from any thread.
     * 添加一个IdleHandler到队列,如果IdleHandler接口方法返回false则执行完会自动删除,
     * 否则需要手动removeIdleHandler。
     */
    public void addIdleHandler(@NonNull IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }

    /**
     * <p>This method is safe to call from any thread.
     * 删除一个之前添加的 IdleHandler。
     */
    public void removeIdleHandler(@NonNull IdleHandler handler) {
        synchronized (this) {
            mIdleHandlers.remove(handler);
        }
    }
    ......
    //Looper的prepare()方法会通过ThreadLocal准备当前线程的MessageQueue实例,
    //然后在loop()方法中死循环调用当前队列的next()方法获取Message。
    Message next() {
        ......
        for (;;) {
            ......
            nativePollOnce(ptr, nextPollTimeoutMillis);
            synchronized (this) {
                ......
                //把通过addIdleHandler添加的IdleHandler转成数组存起来在mPendingIdleHandlers中
                // 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)) {
                    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);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            //循环遍历所有IdleHandler
            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 {
                    //调用IdleHandler接口的queueIdle方法并获取返回值。
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }
                //如果IdleHandler接口的queueIdle方法返回false说明只执行一次需要删除。
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
            ......
        }
    }
}

五、总结

综上所述,在设计延迟加载方案的时候,可以考虑用IdleHandler这种优雅的方式来实现,而不是写死个时间延迟执行。

相关文章

  • Android延迟加载方案之IdleHandler

    一、背景 我们在做启动性能优化的时候,需要尽可能多地减少启动阶段主线程执行的任务时长。对一些非启动阶段一定需要完成...

  • 多线程:6单例模式和多线程

    单例模式和多线程 1.立即加载 2.延迟加载 2.1 延迟加载不同步在多线程环境下的问题 2.2延迟加载的解决方案...

  • 类加载流程

    Android类加载器继承关系 动态加载Dex方案一 动态加载Dex方案二 阅读原文

  • Android 开发集锦

    1.Android拆分与加载Dex的多种方案对比 2.基于Facebook Buck改造Android构建系统之基...

  • android 延迟加载

    转载:http://androidperformance.com/2015/11/18/Android-app-l...

  • 图片延迟加载方案

    图片延迟加载的原理是什么? 图片延迟加载的原理就是先不设置img的src属性,等合适的时机(比如滚动、滑动等)再把...

  • Android IdleHandler

    前面讲Handler的时在MessageQueue源码中看到了IdleHandler的身影 MessageQueu...

  • Android:Handler中的Idle Handler

    Android:Handler中的Idle Handler 抛出 Handler中的IdleHandler 它有什...

  • 懒加载

    什么是懒加载? 懒加载其实就是延迟加载,按需加载,只有在需要时才加载,他是网站的一种优化方案。 我们为什么要使用懒...

  • 懒加载和预加载

    1)概念: 懒加载也叫延迟加载:JS图片延迟加载,延迟加载图片或符合某些条件时才加载某些图片。预加载:提前加载图片...

网友评论

      本文标题:Android延迟加载方案之IdleHandler

      本文链接:https://www.haomeiwen.com/subject/basdxktx.html