前言
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的。