基于稍后在 ViewModel 中启动的其他流程更新现有状态流程

问题描述 投票:0回答:1

我有一个

StateFlow
,它保存
ViewModel
中的 ui 状态。根据一种特定的 ui 状态,进行另一种方法调用,并且此调用返回另一个
Flow
。我想根据 ViewModel 中第二个
StateFlow
的结果更新现有
Flow
(其中一个保存 ui 状态)。

class ScreenViewModel(...) : ViewModel() {

(...)

private val _uiState = MutableStateFlow<ScreenUiState>(ScreenUiState.Loading)
val uiState: StateFlow<ScreenUiState> = _uiState

(...)

private fun doOnSuccess(data: List<Item>) {
_uiState.value = ScreenUiState.Success(data)
fetchNewData()
}

private fun fetchNewData() {
viewModelScope.launch {
repository.getNewData()
//The place where to update ui state with new data...
  }
 }
}


class Repository() {
fun getNewItems(): Flow<List<Item>> {
return dataSource.newItems()
}
}

我见过组合和压缩示例,但它们创建了另一个流程。他们没有解决我的问题。因为我想更新现有的 UiState

Flow
.

一种方法可以是收集第二个

Flow
并更新第一个
Flow
(保存 ui 状态的那个)。但据我了解,在 ViewModel 中不建议进行流量收集。

我该如何解决这个问题?有什么办法可以处理吗?

android kotlin kotlin-coroutines android-viewmodel kotlin-flow
1个回答
0
投票

您是对的,直接在 ViewModel 中收集流可能会导致问题,特别是在生命周期处理和流取消方面。但是,您仍然可以根据另一个

StateFlow
的结果更新现有
Flow
,而无需直接将其收集到 ViewModel 中。处理这种情况的推荐方法是使用
stateIn
onEach
launchIn
结合使用来正确管理流程生命周期,同时仍然更新 UI 状态。

以下是实现此目标的方法:

方法:

  1. 使用
    stateIn
    收集
    ViewModel
    内部的流量,而不阻塞线程
  2. 使用
    launchIn
    异步收集和更新 UI 状态

您可以安全地在后台协程范围中收集第二个

Flow
,更新您的状态,并使用
stateIn
来管理它。

重构代码:

class ScreenViewModel(
    private val repository: Repository
) : ViewModel() {

    private val _uiState = MutableStateFlow<ScreenUiState>(ScreenUiState.Loading)
    val uiState: StateFlow<ScreenUiState> = _uiState

    // Assuming data is updated based on some initial success
    private fun doOnSuccess(data: List<Item>) {
        _uiState.value = ScreenUiState.Success(data)
        fetchNewData()
    }

    private fun fetchNewData() {
        viewModelScope.launch {
            repository.getNewItems()
                .onEach { newItems ->
                    // Update your UI state when new data arrives
                    _uiState.value = ScreenUiState.SuccessWithNewData(newItems)
                }
                .launchIn(viewModelScope) // Collect the flow and update UI state in the ViewModel
        }
    }
}

说明:

  • repository.getNewItems()
    返回
    Flow<List<Item>>
    。此流程会发出新项目,您需要相应地更新
    _uiState
  • onEach { newItems -> ... }
    :此运算符允许您对流中的每个发射做出反应。每当新数据到达时,它都会使用
    _uiState.value = ...
    更新 UI 状态。
  • launchIn(viewModelScope)
    :这会收集 ViewModel 范围内的流,确保正确的生命周期管理。它是非阻塞的,并确保在 ViewModel 被销毁时流程被取消。

为什么有效:

  • 流量是异步收集的,不会阻塞 ViewModel 或应用程序的任何其他部分。
  • ViewModel 中的 UI 状态会更新,以响应第二个
    Flow
    (来自
    repository.getNewItems()
    )。
  • 通过使用
    launchIn(viewModelScope)
    ,您可以确保根据 ViewModel 的生命周期正确管理流集合。

替代方案:

  • combine
    :如果您有两个或更多流(例如,如果您想将初始状态与新数据流合并),您可以使用
    combine
    zip
    ,但在这种情况下,因为您只使用一个额外的流程,如图所示收集它更简单且足够。

此方法对于根据另一个流程的结果更新您的

StateFlow
既安全又有效。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.