如何使用参数收集 ViewModel 中的流

Soh*_*ikh 1 android mvvm coroutine kotlin-flow

我在存储库中有一个函数,它根据参数从网络或远程数据源返回响应。

图书库:

suspend fun getBooks(isNetwork: Boolean) : Flow<Books>
Run Code Online (Sandbox Code Playgroud)

我想在视图模型中收集它,但每次调用 ViewModel 方法时都会创建一个新的协程。我知道我应该在暴露给 UI 层之前映射流程,但是如何将参数传递给函数呢?

图书视图模型:

fun getBooks(isNetwork: Boolean) {
        viewModelScope.launch {
            bookRepository.getBooks(isNetwork).collectLatest { books ->
             ...
            }
        }
}
Run Code Online (Sandbox Code Playgroud)

这里getBooks函数也用于进行网络调用,需要多次调用。每次调用 ViewModel 中的 getBooks 时,都会创建一个新的协程,从而创建多个观察者。

ian*_*ake 5

您根本不应该使用viewModelScope.launch此功能 - 即使您的 UI 消失(即,当您的屏幕进入后退堆栈或用户点击主页按钮时),它也会继续运行。

相反,您需要:

  1. 将您的状态保存isNetwork在 ViewModel 中。公开 setter,以便您的 UI 可以在用户请求网络数据时更新状态。
  2. 使用该isNetwork状态作为调用的输入bookRepository.getBooks
  3. 将最终状态公开给您的 UI,shareIn以确保您始终返回单个 Flow 对象,该对象根据Android SharedFlow 指南在配置更改时保留缓存数据。

这意味着你的ViewModel看起来更像是:

class YourViewModel(
  // Let's assume you are using some sort of dependency injection
  bookRepository: BookRepository
) : ViewModel() {

  // Keep your internal state as an observable MutableStateFlow
  // here defaulting to isNetwork = false
  private val isNetworkFlow = MutableStateFlow(false)

  // Allow your UI to change whether isNetwork is true or false
  public fun useNetwork(isNetwork) {
    isNetworkFlow.value = isNetwork
  }

  // Transform each output of isNetworkFlow into a
  // new Flow from the bookRepository
  public val booksFlow: Flow<List<Books>> = isNetworkFlow.flatMapLatest { isNetwork ->
    bookRepository.getBooks(isNetwork)
  }.shareIn( // Only collect from the booksRepository when the UI is visible
    viewModelScope,
    started = SharingStarted.WhileSubscribed(5000),
    replay = 1
  )
}
Run Code Online (Sandbox Code Playgroud)

现在,只要用户愿意,您的 UI 就可以collect打开并调用以交换到网络源。booksFlowonViewCreateduseNetwork(true)

如果您有更复杂的情况(例如,布尔值可能不够),那么同样的方法也可以用于其他状态。

  • 一般来说,您的存储库层应该已经处理观察它的数据源(例如,每当服务器上的数据发生变化时获取推送消息,从而自动加载新数据),因此整个问题会自行解决,但您可能会考虑遵循最后一段的内容建议并做一些不仅仅是布尔值的事情 - 例如 isNetwork 的数据类 + 对于每个 useNetwork 调用都是唯一的当前时间戳 (2认同)