如何检查线程空闲
在Android冷启动和某些业务场景中,我们期望在主线程不繁忙的情况下请求Api或者初始化SDK等。借助这个策略我们可以很智能的完成一些并不是很重要的任务。很多人马上就想到了MessageQueue.addIdleHandler
。
addIdleHandler
Main线程主要用于UI绘制相关任务,Android提供Looper和MessageQueue来管理这些任务,同时提供了会在UI任务不繁忙的回调接口IdleHandler
。那么能否给子线程也用呢,不可以的,子线程执行的任务不依赖Looper。
java 代码解读复制代码public static interface IdleHandler {
/**
* 当MessageQueue当前没有任务处理时候会回调,而且huidiao运行在主线程
* return true会不断持续监测和回调
* return false 会remove这个回调,调用一次
*/
boolean queueIdle();
}
协程和IdleHandler
知道了api用法我们很容易写出如下代码
kotlin 代码解读复制代码Looper.getMainLooper().queue.addIdleHandler {
// 执行延迟的任务
false
}
当我们有需求例如跟随生命周期销毁,或者添加超时机制那么实现就很麻烦了。但是如果我们借助协程,就会非常简单。首先我们借助suspendCancellableCoroutine
实现一个挂起函数, 调用invokeOnCancellation
在cancel时候remove回调,使用带有生命周期的协程作用域会在销毁时remove回调。
kotlin 代码解读复制代码import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
suspend fun waitForIdle() = suspendCancellableCoroutine {
val queue = Looper.getMainLooper().queue
val callback = {
it.resume(Unit);false
}
queue.addIdleHandler(callback)
it.invokeOnCancellation {
queue.removeIdleHandler(callback)
}
}
测试延迟执行效果, 主线程的挂起并不是阻塞主线程所以不会ANR:
kotlin 代码解读复制代码viewModelScope.launch {
// start time
waitForIdle()
requestRedDot()
//end Log.d("requestRedDot", "after: $time")
}
// requestRedDot - after: 310ms
超时机制 withTimeout
由于这个回调是在主线程不繁忙时候才会回调,我们并不知道它的时间,如果我们有需求最长等待5秒即可执行任务,那么我们可以借助withTimeout
系列函数。
kotlin 代码解读复制代码val t: T? = withTimeoutOrNull(5.seconds) { // T} //5秒后返回null
val t: T = withTimeout(5.seconds) { // T} //5秒后抛出异常
我们需要等待5秒,如果没有执行到回调即可解除addIdleHandler
让挂起函数向后执行。
kotlin 代码解读复制代码suspend fun awaitIdle(timeout: Duration = 5.seconds) = withTimeoutOrNull(timeout) {
waitForIdle()
} ?: Unit
测试下注释掉如下代码,来模拟长时间没有回调
kotlin 代码解读复制代码...
val callback = {
//it.resume(Unit);false
}
// requestRedDot - after: 5000ms
合并
kotlin 代码解读复制代码import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
suspend fun awaitIdle(timeout: Duration = 5.seconds) = withTimeoutOrNull(timeout) {
suspendCancellableCoroutine {
val queue = Looper.getMainLooper().queue
val callback = {
it.resume(Unit);false
}
queue.addIdleHandler(callback)
it.invokeOnCancellation {
queue.removeIdleHandler(callback)
}
}
}
评论记录:
回复评论: