Flow.collect 阻塞主线程

Flow.collect blocking the main thread

我有以下代码似乎阻塞了主线程,即使在 IO 协同程序上调用了流程。我是 kotlin 和 flow 菜鸟。我在这里做错了什么阻塞了主线程?

存储库:

fun observeData(): Flow<Data> {
  return flow {
     //third party api is getting data from a ContentProvider
     ThirdPartyApi.getData().map { convertFromExternalModelToDataModel(it) }
     .collect {
         emit(it)
     }
  }
}

ViewModel:

fun updateUI() {
   scope.launch(Dispatchers.IO) {
     repository.observerData().collect {
       withContext(Dispatchers.Main) {
          textView.text = data.name
       }
     }
   }
}

根据 运行 下面的代码,我看到来自 Android Choreographer 的日志“跳过 200 帧。应用程序在主线程上工作太多”

要在数据流发出时使用 Kotlin Flows 收集数据流,请使用 collect。而由于collect是一个挂起函数,需要在协程中执行。它采用 lambda 作为参数,在每个新值上调用。由于它是一个挂起函数,调用 collect 的协程可能会挂起,直到流程关闭。

而且您不应该在 ViewModel 中更新 UI。

在这种情况下,我们在 activity 的生命周期范围内收集流,该范围是主要安全的并且具有 activity 的生命周期意识。

并使我们的服务或存储库在不同的 CouroutineContext 中执行,使用中间运算符 flowOn

flowOn 更改上游流的 CoroutineContext,这意味着生产者和在 flowOn.

之前(或以上)应用的任何中间运算符

下游流(flowOn 之后的中间运算符和消费者)不受影响,并在用于从流中收集的 CoroutineContext 上执行。

视图模型:

fun getData():Flow<Data> = repository.observeData() // Execute on the io dispatcher
// flowOn affects the upstream flow ↑
.flowOn(Dispatchers.IO)
// the downstream flow ↓ is not affected
.catch { exception -> // Executes in the consumer's context
    emit(Data())
}

Activity:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    
    lifecycleScope.launch { // Consumer's context
        viewModel.getData().collect { // Suspended
            textView.text = data.name // Collect on consumer's context
        }
    }
}