Kotlin流-收集时未接收到某些已发出的事件

hfyxw5xn  于 2023-11-21  发布在  Kotlin
关注(0)|答案(3)|浏览(196)

我使用的是MutableStateFlow,我的流类型是密封类,有不同的状态(Loading,Success,Error等)。我的流的初始值为空:

private val _updateDepartmentsState = MutableStateFlow<DepartmentFetchState>(DepartmentFetchState.Empty)

字符串
在我的仓库中,我发出不同的状态。例如:

suspend fun updateDepartments() {
      _updateDepartmentsState.emit(DepartmentFetchState.Loading)
      try {
        remoteDataSource.updateDepartments()
        // here some code
        _updateDepartmentsState.emit(DepartmentFetchState.Success(data))
      } catch(e: NetworkException) {
         _updateDepartmentsState.emit(DepartmentFetchState.Error)
       }
    }


在我的仓库中,我也有只读流:

val updateDepartmentsState = _updateDepartmentsState.asStateFlow()


在视图模型中,我通过交互器收集流。我在视图模型中的代码:

updateDepartmentsState.emitAll(
            interactor
                .updateState // state flow (`updateDepartmentsState` ) from repository via interactor
                .map { state->
                    when (state) {
                        DepartmentFetchState.Loading -> {}
                        DepartmentFetchState.Error-> {}
                        ...
  }
                }.also {
                    interactor.updateDepartments() // call updateDepartments() from repository via interator
                }


我从文档中了解到,在我们完成收集之后,我们必须得到初始值。但是它没有发生。而且,我没有收到状态DepartmentFetchState.Loading。我只收到最后一个状态-DepartmentFetchState.Success
但最有趣的是,如果我从视图模型重新调用代码(例如,当通过滑动更新时),那么我会得到DepartmentFetchState.Loading状态,然后是DepartmentFetchState.Success状态,正如预期的那样。
我不明白为什么在第一次调用时,初始化流时设置的初始值和DepartmentFetchState.Loading状态丢失了。
请帮帮我(

slsn1g29

slsn1g291#

您所描述的是StateFlow的预期用途。
此外,我没有接收到状态DepartmentFetchState.Loading。我只接收到最后一个状态-DepartmentFetchState.Success
这是因为StateFlow是一个状态持有者可观察的流,它将当前和新的状态更新发送到它的收集器。当你开始收集流时,你的updateDepartments()已经完成了DepartmentFetchState.Success。这意味着从你收集流的这一刻起,当前状态是DepartmentFetchState.Success
然后:
但最有趣的是,如果我从视图模型重新调用代码(例如,当通过滑动更新时),那么我会得到DepartmentFetchState.Loading状态,然后是DepartmentFetchState.Success状态,正如预期的那样。
当您重新调用代码时,您会收到预期的结果,这是因为您在执行updateDepartments()时一直在从流中收集数据,它将分别发出当前状态DepartmentFetchState.LoadingDepartmentFetchState.Success

arknldoa

arknldoa2#

您可以在emit之后添加一个yield(),这将允许收集器运行它的collect方法,如下所示:

suspend fun updateDepartments() {
      _updateDepartmentsState.emit(DepartmentFetchState.Loading)
      try {
        remoteDataSource.updateDepartments()
        // here some code
        _updateDepartmentsState.emit(DepartmentFetchState.Success(data))

        yield() // <-- allows the collector to run after each update
      } catch(e: NetworkException) {
         _updateDepartmentsState.emit(DepartmentFetchState.Error)
       }
    }

字符串
我不知道为什么你需要在你的情况下这样做,但是当你想以某种方式处理每个发射时,它是有用的,所以它确实有一个很好的用例。
只要知道任何处理都将被迫重新处理,可能不必要地使用这种方法。

cu6pst1q

cu6pst1q3#

这是因为StateFlow对值的更新总是合并的。如果值的发布速度比收集速度快,那么收集器只会得到最新的结果。
我觉得你有两个选择:
1.在更新后使用一点延迟,以确保它不会被跳过(* 不可靠 *)
1.使用SharedFlow代替,它不比较值并发出每个更新。

相关问题