【Android】Kotlin.flow在主线程collect为什么不阻塞主线程?
一,问题
问题,若在Activity中启动一个协程,指定上下文Dispatcher在主线程,而collect又是挂起方法,为什么不会阻塞主线程?
class DemoMainActivity: FragmentActivity() {private val mShareFlow: MutableSharedFlow<String> = MutableSharedFlow(replay = 1, extraBufferCapacity = 0, onBufferOverflow = BufferOverflow.SUSPEND)override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {super.onCreate(savedInstanceState, persistentState)lifecycleScope.launch(Dispatchers.Main) {mShareFlow.collect {//等待mShareFlow.emit数据处理}}}
}
二,分析
kotlin协程存在挂起、恢复点,内部通过状态机管理各种挂起状态。这里的collect就是挂起点,
反编译DemoMainActivity,可以看到如下内容
...
public final class DemoMainActivity extends FragmentActivity {@NotNullprivate final MutableSharedFlow mShareFlow;public DemoMainActivity() {this.mShareFlow = SharedFlowKt.MutableSharedFlow(1, 0, BufferOverflow.SUSPEND);}public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {super.onCreate(savedInstanceState, persistentState);BuildersKt.launch$default((CoroutineScope)LifecycleOwnerKt.getLifecycleScope((LifecycleOwner)this), (CoroutineContext)Dispatchers.getMain(), (CoroutineStart)null, new Function2((Continuation)null) {int label;public final Object invokeSuspend(Object $result) {Object var2 = IntrinsicsKt.getCOROUTINE_SUSPENDED();switch (this.label) {case 0:ResultKt.throwOnFailure($result);MutableSharedFlow var10000 = DemoMainActivity.this.mShareFlow;FlowCollector var10001 = null.INSTANCE;Continuation var10002 = (Continuation)this;this.label = 1;if (var10000.collect(var10001, var10002) == var2) {return var2;}break;case 1:ResultKt.throwOnFailure($result);break;default:throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");}throw new KotlinNothingValueException();}public final Continuation create(Object value, Continuation $completion) {return (Continuation)(new <anonymous constructor>($completion));}public final Object invoke(CoroutineScope p1, Continuation p2) {return ((<undefinedtype>)this.create(p1, p2)).invokeSuspend(Unit.INSTANCE);}// $FF: synthetic method// $FF: bridge methodpublic Object invoke(Object p1, Object p2) {return this.invoke((CoroutineScope)p1, (Continuation)p2);}}, 2, (Object)null);}
}
以上,collect编译后放在了invokeSuspend回调中(case 0 状态点),这在协程resume时调用。我们写代码的时候看似同步逻辑,其实在编译后都是各种invokeSuspend回调,这就回答了为什么不阻塞主线程。
那么主线程如何执行这个suspend点?很简单,通过MainHandler,
BuildersKt.launch$default((CoroutineScope)LifecycleOwnerKt.getLifecycleScope((LifecycleOwner)this), (CoroutineContext)Dispatchers.getMain(), (CoroutineStart)null, new Function2((Continuation)null)
Dispatchers.getMain()指定了一个invokeSuspend处理线程,即主线程。这里通过工厂方法返回一个HandlerDispatcher,如下,
在dispatch通过handler#post执行block逻辑,如下
这样就能通过Android#Handler机制在主线程处理suspend状态点了。
三,最后
本文分析基于以下kotlin协程实现分析,请参考
【Android】kotlin协程实现原理-CSDN博客