目录

用IdleHandler来性能优化及原理源码分析

用IdleHandler来性能优化及原理源码分析

背景:

经常在做一些app冷启动速度优化等性能优化工作时候,经常可能会发现有时候需要引入一些第三方sdk,或者库,这些库一般会要求我们在onCreate中进行初始化等,但是onCreate属于生命周期的回调方法,如果onCreate中业务过多就会影响整个app的冷启动时长,很多人这个时候第一想到可能是放入子线程等,但是经常又发现有的调用还要求在主线程调用等限制,所以针对这种情况下就经常会使用到一个IdleHandler,这个当初使用时候大家可能就只是字面意思理解为空闲的Handler,在空闲时候执行,其实没有深入去看看它到底实现原理是什么,今天刚好有机会就来给大家进行一个剖析。

IdleHandler 的概念

IdleHandler 是 MessageQueue 提供的一个接口,用于监听消息队列的空闲状态。它允许开发者在主线程处于空闲时,执行一些低优先级的任务。IdleHandler 的核心方法 queueIdle() 会在系统检测到消息队列空闲时调用。返回值决定了 IdleHandler 是否继续在队列中保留:若返回 true,则该回调在下次队列空闲时继续执行;反之,返回 false 则表示仅执行一次后移除。

IdleHandler 和性能优化

IdleHandler 的出现正好迎合了性能优化的需求。在实际开发中,有一些任务并非必须实时完成,例如日志上报、资源预加载、缓存清理、数据统计、一些动画或预渲染任务等。利用 IdleHandler,可以将这些不紧急的任务推迟到主线程消息队列空闲时再执行,从而避免干扰用户看到的实时界面更新,提高整体界面流畅度和响应速度。因此,在 UI 主线程相对繁忙时,通过 IdleHandler 来分摊任务,可以让系统先处理用户的核心交互,就比如onCreate是生命周期方法,如理里面初始化太多东西影响冷启动速度,针对一些可以延后不那么紧急任务可以待系统空闲时再处理任务,充分利用 CPU 空闲时间。

源码分析IdleHandler

为了更加全面了解IdleHandler,下面将要从以下几个部分进行IdleHandler的源码分析。

1、

IdleHandler的接口解释:

frameworks/base/core/java/android/os/MessageQueue.java

    /**
     * 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属于一个回调接口,这个接口在线程即将要阻塞等待更多消息时候会被调用。

queueIdle就是对应的回调函数,这个queueIdle有一个返回值true或false,这两个值分别有如下区别:

如果返回true意味着下一次空闲依旧会执行这个IdleHandler的回调函数。

如果返回false就代表当前IdleHandler已经执行完毕,下次空闲不会再执行该IdleHandler。

添加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这个集合中
            mIdleHandlers.add(handler);
        }
    }

这里的addIdleHandler添加就是简单加入的mIdleHandlers这个集合中,方便后面执行时候进行遍历

IdleHandler的触发执行

    @UnsupportedAppUsage
    Message next() {
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
           //进入等待消息,要被唤醒2种情况,1被其他函数wake,2等待时间nextPollTimeoutMillis到
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                //当消息的Handler为空时,则查询异步消息
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                     //当查询到异步消息,则立刻退出循环
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
               	 //检测msg时间是还没到了
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        //还没到msg的处理时间,那么就会与当前时间进行差值,这个差值就是等待时间nextPollTimeoutMillis
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                        //设置下一个消息为mMessages
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        msg.markInUse();
                        //直接return回去消息,也就是有消息都在这里return了不会执行下面业务
                        return msg;
                    }
                } else {
                //没有找到消息
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // 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.
                //找了一圈发现没有要处理的消息了,那么就开始判断是否有IdleHandler的消息要处理
                //注意这里的pendingIdleHandlerCount第一次进入才是-1,后续一旦执行IdleHandler都是变成0,不会再进入
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    //把mIdleHandlers集合大小赋值给pendingIdleHandlerCount
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                //如果发现没有IdleHandler,那么就返回循环进行block阻塞等待唤醒
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }
							//如果mPendingIdleHandlers这个数组没有初始化就进行初始化,这里最小大小为4应该是为了性能以防频繁扩容
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                //mIdleHandlers转成了数组mPendingIdleHandlers
                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的对应回调业务方法
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }
								//这里的keep就是queueIdle的返回值,如果返回false就会从mIdleHandlers进行移除
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
						//把pendingIdleHandlerCount变成0,不是-1,这样防止死循环执行IdleHandler
            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;
					//这里是为了让尽快遍历一下是否有消息,因为可能执行IdleHandler期间有新消息
            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

上面next方法,关于IdleHandler执行总结如下:

1、next方法遍历消息队列,确实没有寻找到要处理的消息任务

2、没有要处理的消息则开始处理IdleHandler相关的任务

3、如果IdleHandler的queueIdle返回false则会从mIdleHandlers删除,下次空闲就不会在执行这个IdleHandler,否则true的话会每次空闲都执行

实战案例

测试一下queueIdle返回false,即只执行一次queueIdle

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        getMainLooper().getQueue().addIdleHandler(new MessageQueue.IdleHandler() {
            @Override
            public boolean queueIdle() {
                //TODO 执行一些不那么紧急任务                
                Trace.beginSection("queueIdle no next runing");
                Log.i("lsm66666","queueIdle return false,no next runing");
                Trace.endSection();
                return false;
            }
        });
    }

验证结果:

https://i-blog.csdnimg.cn/direct/1421054834384f23b0b2173a883bf9d2.png

再测试一下queueIdle返回true,即执行多次queueIdle

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        getMainLooper().getQueue().addIdleHandler(new MessageQueue.IdleHandler() {
            @Override
            public boolean queueIdle() {
                //TODO 执行一些不那么紧急任务                
                Trace.beginSection("queueIdle has next runing");
                Log.i("lsm66666","queueIdle return true,has next runing");
                Trace.endSection();
							return true;
            }
        });
    }

看看日志打印情况

https://i-blog.csdnimg.cn/direct/379a0240e8aa4bc0ac198ecd02207fb4.png

systrace结合分析IdleHandler

https://i-blog.csdnimg.cn/direct/4381b11a96c0482382d8986735d45704.png

上面也可以看出来IdleHandler的执行都在没有消息可以处理了,才会执行,而且执行完成后就进入消息等待时期。

从上面也可以看出来,IdleHandler确实是在主线程空闲暂时没有消息处理时候进行执行的,可以起到一个错峰执行的目的,不过也要注意以下几点:

1、切勿让IdleHandler执行耗时操作,应该是可以快速完成的一些任务

2、尽量都让IdleHandler执行是一次性任务,即queueIdle返回false,以防每次空闲都有重复调用

文章参考:

更多framework实战开发干货,请关注下面“千里马学框架”