携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第9天,点击查看活动详情
书接上回,上一篇文章我们分析了
Dispatchers.Main是个啥,如何创建的,接下来我们就来回答下面两个问题:
历史
肢解LiveData:协程味的CoroutineLiveData了解一下(一)
Dispatchers.Main如何运用?
先说下结论:Dispatchers.Main即HanlerContext最后会被包装成一个DispatchedContinuation对象,接下来我们以lifecycleScope.launch(Dispatchers.Main){}为入口分析这个包装过程:
DispatchedContinuation创建流程
CoroutineScope.launch
其中context被指定为Dispatchers.Main,start.isLazy默认为false,最终会创建StandaloneCoroutine对象。
StandaloneCoroutine.start()
CoroutineStart.start()
启动模式不指定默认为DEFAULT,最终调用startCoroutineCancellable()方法。
(suspend () -> T).startCoroutineCancellable
我们重点关注中间的intercepted()方法。
Continuation<T>.intercepted()
ContinuationImpl.intercepted
其中context[ContinuationInterceptor]就是我们之前传入的Dispatchers.Main即HandlerContext对象,我们看下HandlerContext的interceptContinuation()方法。
interceptContinuation()
最终创建了DispatchedContinuation对象并把我们的HandlerContext进行传入。
DispatchedContinuation.resumeCancellableWith()执行协程
还记得上面我们调用lifecycleScope.launch最终会走到下面流程:
最终会调用DispatchedContinuation.resumeCancellableWith方法开始协程的真正执行:
inline fun resumeCancellableWith(
result: Result<T>,
noinline onCancellation: ((cause: Throwable) -> Unit)?
) {
val state = result.toState(onCancellation)
//1.是否需要分发
if (dispatcher.isDispatchNeeded(context)) {
_state = state
resumeMode = MODE_CANCELLABLE
//2.分发协程到指定线程执行
dispatcher.dispatch(context, this)
} else {
executeUnconfined(state, MODE_CANCELLABLE) {
if (!resumeCancelled(state)) {
//3.不分发直接执行协程
resumeUndispatchedWith(result)
}
}
}
}
dispatcher就是我们传过来的调度器HanlerContext:
- 调用
isDispatchNeeded判断是否需要分发到执行线程执行:
override fun isDispatchNeeded(context: CoroutineContext): Boolean {
return !invokeImmediately || Looper.myLooper() != handler.looper
}
invokeImmediately之前分析的是false,所以该方法的返回值直接就是true,即默认需要分发到主线程执行:
不管当前是不是主线程环境,都重新分发到主线程,就相当于即使我已经在主线程了,但更新UI还是要通过Handler发送一个message到主线程Looper,然后再由Looper分发到主线程执行,有点多此一举了,所以协程官方提供了Dispatchers.Main.immediate避免此种情况发生。
- 执行
dispatch()分发协程
override fun dispatch(context: CoroutineContext, block: Runnable) {
if (!handler.post(block)) {
cancelOnRejection(context, block)
}
}
很简单,就是利用handler将该协程块封装成一个message发送到主线程执行。顺便说一嘴,这个传入的Runnable对象的run方法是定义在DispatchedTask(DispatchedContinuatio父类)类中的:
具体的我们就不进行分析了,感性的自行查看源码!
Dispatchers.Main.immediate和Dispatchers.Main又有啥区别?
immediate看到是Dispatchers.Main的一个属性,我们直接看下Dispatchers.Main实现类HandlerContext对这个属性进行了重写:
override val immediate: HandlerContext = _immediate ?:
HandlerContext(handler, name, true).also { _immediate = it }
可以看到immediate也是一个HandlerContext对象,和Dispatchers.Main的HandlerContext对象不同的是,第三个参数invokeImmediately传入了true,根据我们上面的分析,这个参数的值会影响到协程是否需要分发执行:
override fun isDispatchNeeded(context: CoroutineContext): Boolean {
return !invokeImmediately || Looper.myLooper() != handler.looper
}
invokeImmediately为true,那么isDispatchNeeded()方法返回值由Looper.myLooper() != handler.looper决定,而handler.looper代表主线程Looper,所以isDispatchNeeded()该方法返回值代表当前协程的创建线程是否已经是主线程了,是的话直接进行协程块的执行而不用再经过调度器进行分发了。
所以说Dispatchers.Main.immediate是Dispatchers.Main的一个小优化,如果创建协程线程和协程调度器线程都是主线程的话,省去一个调度去分发的过程。