SharedFlow 不从发射中收集

Nis*_*lan 7 android kotlin kotlin-coroutines kotlin-flow kotlin-sharedflow

在我的 ViewModel 中,我正在发出 API 请求,并且正在使用 FragmentStateFlowSharedFlow与 Fragment 进行通信。在发出 API 请求时,我可以轻松更新状态流的值,并将其成功收集到片段中。

但在发出请求之前,我发出了一些布尔值,SharedFlow并且它没有收集在片段中。有人可以帮助我为什么会发生这种情况吗?

class MainViewModel: ViewModel() {
  private val _stateFlow = MutableStateFlow(emptyList<Model>())
  val stateFlow = _stateFlow.asStateFlow()

  private val _loading = MutableSharedFlow<Boolean>()
  val loading = _loading.asSharedFlow()

  suspend fun request() {
    _loading.emit(true)
    withContext(Dispatchers.IO) {
      /* makes API request */
      /* updates _stateFlow.value */
      /* stateFlow value is successfully collected */
    }
    _loading.emit(false) // emitting boolean value
  }
}
Run Code Online (Sandbox Code Playgroud)
class MyFragment : Fragment(R.layout.fragment_my) {
   // ...

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    lifecycleScope.launchWhenStarted {
      viewModel.request()
      /* stateFlow is collected and triggered - working properly */

      viewModel.loading.collectLatest { // <- NOT COLLECTING - WHY?
        Log.d(this::class.simpleName, "onViewCreated: $it") // <- NOT LOGGING
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Coo*_*ind 9

SharedFlow是一股热流。也许你应该创建它

MutableSharedFlow(
    replay = 0,
    onBufferOverflow = BufferOverflow.DROP_OLDEST,
    extraBufferCapacity = 1
)
Run Code Online (Sandbox Code Playgroud)

或者

MutableSharedFlow(
    replay = 1,
    onBufferOverflow = BufferOverflow.DROP_OLDEST
)
Run Code Online (Sandbox Code Playgroud)


Ser*_*gey 2

我想您需要启动一个不同的协程来收集加载值,如下所示:

 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    lifecycleScope.launchWhenStarted {
      viewModel.request()
    }
    lifecycleScope.launchWhenStarted {    
      viewModel.loading.collectLatest { 
        Log.d(this::class.simpleName, "onViewCreated: $it")
      }
    }
  }
Run Code Online (Sandbox Code Playgroud)

viewModel.request()function 是一个suspend函数,它会挂起协程直到完成。但我猜它由于调用suspendfunction 而没有完成_loading.emit(),挂起直到它被收集。


或者我认为更好的是在ViewModel课堂上启动一个协程,如下所示:

// In MainViewModel
fun request() = viewModelScope.launch {
    _loading.emit(true)
    withContext(Dispatchers.IO) {
      /* makes API request */
      /* updates _stateFlow.value */
      /* stateFlow value is successfully collected */
    }
    _loading.emit(false) // emitting boolean value
}

// In MyFragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    lifecycleScope.launchWhenStarted {
      viewModel.loading.collectLatest { 
        Log.d(this::class.simpleName, "onViewCreated: $it") 
      }
    }

    viewModel.request()
}
Run Code Online (Sandbox Code Playgroud)