【Handler】IdleHandler

MessageQueue 空闲的时候,会调用 IdleHandler 里面的方法,可用于启动优化,利用空闲时间加载配置,避免卡住 UI 的展示。

使用场景

1.Activity 的生命周期回调 #onStop 和 #onDestroy

2.启动优化(WebView的引擎提前加载)

booster ShadowWebView

基础使用

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    
    
    mainLooper.queue.addIdleHandler {
    
    
        //加载闲时任务
        false
    }
}

返回值 false 表示任务只会执行一次
返回值 true 表示任务会执行多次

原理

@UnsupportedAppUsage
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();

@UnsupportedAppUsage
Message next() {
    
    
    // Return here if the message loop has already quit and been disposed.
    // This can happen if the application tries to restart a looper after quit
    // which is not supported.
    final long ptr = mPtr;
    if (ptr == 0) {
    
    
        return null;
    }

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

        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
    
    
            ...
            // 如果第一次空闲,那么获取要运行的空闲程序的数量。
            // 空闲句柄仅在队列为空或第一条消息时运行
            // 在队列中(可能是一个同步屏障)是由于在将来被处理。
            if (pendingIdleHandlerCount < 0
                && (mMessages == null || now < mMessages.when)) {
    
    
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
    
    
                //没有空闲的处理程序可以运行。循环并等待更多时间。
                mBlocked = true;
                continue;
            }

            if (mPendingIdleHandlers == null) {
    
    
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        // 运行空闲处理程序。
        // 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();
            } catch (Throwable t) {
    
    
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }

            if (!keep) {
    
    
                synchronized (this) {
    
    
                    mIdleHandlers.remove(idler);
                }
            }
        }

        // Reset the idle handler count to 0 so we do not run them again.
        pendingIdleHandlerCount = 0;

        // 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;
    }
}

判断消息队列当前没有消息的时候会执行 mIdleHandlers 中的任务

消息队列是空的时候,插入一个屏障,会触发IdleHandler么?

答:不会,插入屏障,并不会唤醒线程

如果删除了屏障,消息队列空了,会触发IdleHandler么?

答:不会。处理完消息,发现目前没有消息要处理(也可以是触发时间没到),触发IdleHandler,处理完毕。会再次检查是否有消息要处理,因为IdleHandler触发时,有可能有新的消息插入消息队列。如果没有,进入休眠,再次被唤醒时,不会再次(重复)触发IdleHandler。简单的说就是消息队列处理完最后一个消息,休眠之前调用过IdleHandler,休眠之后被唤醒,还是没有要处理的消息,不会重复调用IdleHandler

如果消息队列只有一个屏障消息,插一个普通消息会触发IdleHandler么?

答:有可能。IdleHandler的触发条件是,消息队列为空,或者第一条消息的触发时间还没到。所以如果屏障的超时时间还没有到,也就是目前还没有消息要处理,会触发IdleHandler

如果消息队列只有一个屏障消息,插入一个异步消息会触发IdleHandler么?

答:有可能,关键看屏障的触发时间到了没有,如果没有到,就会触发IdleHandler,反之就不会。

注意事项

1.不要加载耗时任务

耗时任务会卡住其他任务的执行

2.不要添加太多任务

闲时加载的时候,会一次性处理完所有的闲时任务,时间太长会导致 ANR

资料

[1].IdleHandler你会用吗?记一次IdleHandler使用误区,导致ANR
[2].Handler之消息屏障你应该知道的

猜你喜欢

转载自blog.csdn.net/Android_yh/article/details/130611623