IdleHandler的使用及原理

IdleHandler方式就是利用其特性,只有CPU空閑的時(shí)候才會(huì)執(zhí)行相關(guān)任務(wù),并且我們可以分批進(jìn)行任務(wù)初始化,可以有效緩解界面的卡頓。

簡(jiǎn)單用法代碼如下:

        Looper.myQueue().addIdleHandler(object: MessageQueue.IdleHandler {
            override fun queueIdle(): Boolean {
                //執(zhí)行任務(wù)
                return false;
            }
        })

可以將上述代碼添加到Activity onCreate中,在queueIdle()方法中實(shí)現(xiàn)延遲執(zhí)行任務(wù),在主線程空閑,也就是activity創(chuàng)建完成之后,它會(huì)執(zhí)行queueIdle()方法中的代碼。

如何設(shè)置是否重復(fù)執(zhí)行

queueIdle()返回true表示可以反復(fù)執(zhí)行該方法,即執(zhí)行后還可以再次執(zhí)行;返回false表示執(zhí)行完該方法后會(huì)移除該IdleHandler,即只執(zhí)行一次。

IdleHandler源碼解析:

IdleHandler屬于MessageQueue內(nèi)部接口,只有一個(gè)queueIdle()方法聲明。通過(guò)方法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方法何時(shí)執(zhí)行

在MessageQueue取消息的next方法中,IdleHandler相關(guān)代碼如下:

    @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)) {
                   //如果消息隊(duì)列為空或者消息執(zhí)行時(shí)間還未到,則獲取IdleHandler隊(duì)列的大小,下面需要用到
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                   //無(wú)需要執(zhí)行的  idle handler,則繼續(xù)阻塞
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                //將IdleHandler列表轉(zhuǎn)為數(shù)組
                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();   //開(kāi)始順序執(zhí)行所有IdleHandler的queueIdle方法
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {   //如果發(fā)現(xiàn)有queueIdle()方法返回false,則線程安全地刪除這個(gè)idlehandler不再執(zhí)行queueIdle
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
            ...
        }
    }
多任務(wù)延遲初始化實(shí)戰(zhàn):

我們根據(jù)queueIdle返回true時(shí)可以執(zhí)行多次的特點(diǎn),可以實(shí)現(xiàn)一個(gè)任務(wù)列表,然后從這個(gè)任務(wù)列表中取任務(wù)執(zhí)行。

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任務(wù)不為空,就繼續(xù)執(zhí)行初始化
    };

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

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

創(chuàng)建一個(gè)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");
    }
}

界面顯示后進(jìn)行調(diào)用:

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

打印執(zhí)行結(jié)果


關(guān)于IdleHandler問(wèn)題

Q:IdleHandler 有什么用?

IdleHandler 是 Handler 提供的一種在消息隊(duì)列空閑時(shí),執(zhí)行任務(wù)的時(shí)機(jī);
當(dāng) MessageQueue 當(dāng)前沒(méi)有立即需要處理的消息時(shí),會(huì)執(zhí)行 IdleHandler;

Q:MessageQueue 提供了 add/remove IdleHandler 的方法,是否需要成對(duì)使用?

不是必須;
IdleHandler.queueIdle() 的返回值,可以移除加入 MessageQueue 的 IdleHandler;
Q:當(dāng) mIdleHanders 一直不為空時(shí),為什么不會(huì)進(jìn)入死循環(huán)?

只有在 pendingIdleHandlerCount 為 -1 時(shí),才會(huì)嘗試執(zhí)行 mIdleHander;
pendingIdlehanderCount 在 next() 中初始時(shí)為 -1,執(zhí)行一遍后被置為 0,所以不會(huì)重復(fù)執(zhí)行;
Q:是否可以將一些不重要的啟動(dòng)服務(wù),搬移到 IdleHandler 中去處理?

不建議;
IdleHandler 的處理時(shí)機(jī)不可控,如果 MessageQueue 一直有待處理的消息,那么 IdleHander 的執(zhí)行時(shí)機(jī)會(huì)很靠后;
Q:IdleHandler 的 queueIdle() 運(yùn)行在那個(gè)線程?

陷進(jìn)問(wèn)題,queueIdle() 運(yùn)行的線程,只和當(dāng)前 MessageQueue 的 Looper 所在的線程有關(guān);
子線程一樣可以構(gòu)造 Looper,并添加 IdleHandler;

參考:
https://juejin.cn/post/7055564669540368392

Github代碼地址:

https://github.com/running-libo/PerformanceOpt

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容