Dispatcher.Main的Handler设计成异步消息

616 阅读1分钟

前言

MainScope里挂起回调也是通过Handler到主线程looper中执行的。 两种方式实现ui更新间隔1ms,去刷新view的y高度 Handler.postDelay和MainScope.launch{delay()}。

Handler.postDelay每次间隔11ms打印一次(90Hz刷新率)。每次都在onDraw之后也就是一帧才移动/绘制一次。

move方法里有个invalidata(),这里会加入一个屏障消息,等待下一帧信号来的时候,会执行View的绘制,并且会移除屏障消息,所以,这里的消息每隔一帧才会执行一次。

MainScope基本间隔1ms,每一帧绘制7次左右,比Handler.postDelay快7倍左右。

# Delay.kt
public suspend fun delay(timeMillis: Long) {
    if (timeMillis <= 0) return // don't delay
    return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
        // if timeMillis == Long.MAX_VALUE then just wait forever like awaitCancellation, don't schedule.
        if (timeMillis < Long.MAX_VALUE) {
            cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
        }
    }
}

# HandlerDispatcher.kt
override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
    val block = Runnable {
        with(continuation) { resumeUndispatched(Unit) }
    }
    if (handler.postDelayed(block, timeMillis.coerceAtMost(MAX_DELAY))) {
        continuation.invokeOnCancellation { handler.removeCallbacks(block) }
    } else {
        cancelOnRejection(continuation.context, block)
    }
}

override fun createDispatcher(allFactories: List<MainDispatcherFactory>): MainCoroutineDispatcher {
    val mainLooper = Looper.getMainLooper() ?: throw IllegalStateException("The main looper is not available")
    // handler设置异步消息
    return HandlerContext(mainLooper.asHandler(async = true))
}

可以看到MainScope通过delay发送的是异步消息,async=true。那么遇到消息屏障后,此异步消息会正常执行。

一次如果把上面的Handler发送改为异步消息也是可以实现1ms一次更新ui的。