StateFlowImpl.collect含while循环,UI线程调用为何不阻塞?循环何时退出?
StateFlow的collect内部死循环不阻塞UI线程的原因及循环退出条件
为什么两个死循环表现差异巨大?
两个while(true)的核心差异在于是否会主动让出线程执行权:
- 你自定义的
while(true)代码块中没有任何挂起操作,会持续占用Main线程的执行资源。Android主线程是消息驱动的,必须轮询处理消息队列中的UI事件、点击事件等任务,而协程的调度是协作式的,你不主动让出线程,调度器就没有机会将主线程切去处理其他任务,自然会导致点击无响应,最终触发ANR。 - StateFlow的
collect方法内部的while(true)中存在挂起逻辑:你看源码中的slot.awaitPending()就是挂起函数,当没有新的状态更新时,这个方法会挂起当前协程,主动让出主线程的执行权,主线程就可以正常处理UI交互等任务。只有当StateFlow有新的状态值更新时,这个挂起的协程才会被恢复,继续执行下一轮循环,所以全程不会阻塞主线程。
collect内部的while循环什么时候退出?
只有两种情况会终止这个循环:
- 协程所属的Job被取消:源码中每轮循环都会执行
collectorJob?.ensureActive(),如果当前协程的Job已经处于非活跃状态,这个方法会直接抛出CancellationException,终止循环并进入finally代码块释放资源。比如你手动调用collect所在的launch返回的Job的cancel()方法,或者绑定的MainScope在Activity销毁时自动取消所有子协程,都会触发这个逻辑。 - collect代码块抛出未捕获异常:如果你在
collect{ }的代码块中抛出了没有捕获的异常,也会直接终止循环,结束collect流程。
参考代码块
测试代码
class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() { private val TAG = "MainActivity" val flow = MutableStateFlow(0) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) launch { while (true) { Log.d(TAG, "while") } } launch { flow.collect { Log.d(TAG, "onCreate: $it") } } } }
StateFlowImpl核心源码
// This is StateFlowImpl override suspend fun collect(collector: FlowCollector<T>) { val slot = allocateSlot() try { if (collector is SubscribedFlowCollector) collector.onSubscription() val collectorJob = currentCoroutineContext()[Job] var oldState: Any? = null // previously emitted T!! | NULL (null -- nothing emitted yet) while (true) { val newState = _state.value collectorJob?.ensureActive() if (oldState == null || oldState != newState) { collector.emit(NULL.unbox(newState)) oldState = newState } if (!slot.takePending()) { slot.awaitPending() } } } finally { freeSlot(slot) } }
内容的提问来源于stack exchange,提问作者summer
相关产品推荐
相关产品推荐

