【问题标题】:How to manually update a kotlin flow如何手动更新 kotlin 流程
【发布时间】:2021-11-01 17:38:15
【问题描述】:

我正在尝试在 Kotlin 中更新 Flow 的源代码,但我不确定这是否是正确的方法,以及 Flow 是否可行。

我有一个包含用户帖子的数据库,这会返回一个Flow<List<Post>>。 现在,当我选择另一个用户时,我希望数据库流向我返回新选择的用户的帖子:


lateinit var userPosts: Flow<List<Post>>
   private set

fun getPostsForUser(user: User) {
   userPosts = database.getAllPostsForUser(user)
}

但流程永远不会使用新选择的用户的数据进行更新。在这种情况下,Flow 仍然是正确的选择吗?如果是,我如何使用新帖子更新流程?

我知道如何手动从数据库中获取数据并使用 LiveData 发出它,但我想避免在每次用户发布新内容或删除帖子时处理帖子更新。

【问题讨论】:

  • 我有点不确定你在最后一句话中描述了什么。你能详细说明吗?我认为 Flow 和 LiveData 具有相同的行为,因为每次 Room 数据库更改时都会更新,但使用 Flow,您可以使用 distinctUntilChanged() 或 StateFlow 来避免这种情况。
  • 因为当我在房间数据库上运行的查询发生更改时,Flow 会自动发出值,例如删除或添加用户的帖子,我不必手动检查这些更改.到目前为止,我正在为我的数据库查询使用挂起函数并发布值,然后使用实时数据,因为我只需要当前数据的快照。
  • 如果您在发出请求时只想要快照,那么挂起函数是正确的方法。 Flow 在行为上类似于 LiveData,就触发更新而言,尽管您可以更轻松地过滤结果。

标签: kotlin android-room


【解决方案1】:

我认为您可能正在收集一个流,然后设置一个用户,这会更改属性中的流,但不会更改已收集的任何现有的先前流。我不知道还能如何解释正在发生的事情。

拥有一个依赖于其他接受参数的函数的公共 Flow 属性可能容易出错。您可以直接从函数返回 Flow ,因此行为不会有歧义。该片段为特定用户请求一个流并立即获取它。

distinctUntilChanged() 将阻止它发出由于 repo 中的其他更改而导致的未更改列表。

fun getPostsForUser(user: User) Flow<List<Post>> =
    database.getAllPostsForUser(user).distinctUntilChanged()

如果您确实想使用您的模式,我认为您可以这样做。这允许只有一个 Flow,因此尽早开始收集它是安全的。更改用户将更改其发布的值。虽然这比上面更复杂,但它的优点是不需要在屏幕旋转和其他配置更改时刷新数据。

private val mutableUserPosts = MutableStateFlow<List<Post>>(emptyList())
val userPosts: Flow<List<Post>> = mutableUserPosts

private var userPostsJob: Job? = null
var user: User? = null
    set(value) {
        field = value
        userPostsJob?.cancel()
        value ?: return
        userPostsJob = database.getAllPostsForUser(value)
            .onEach { mutableUserPosts.emit(it) }
            .launchIn(viewModelScope)
    }

或者正如 Joffrey 建议的那样,如果您不介意使用不稳定的 API 函数 flatMapLatest,使用用户流会更简单。如果您不介意公开应在外部设置值的公共可变流,则可以删除此处的 user 属性。或者,如果您重复使用此模式,您可以为 StateFlow/MutableStateFlow 制作运算符扩展函数,以将其用作属性委托。

private val userFlow = MutableStateFlow<User?>(null)
var user: User?
    get() = userFlow.value
    set(value) {
        userFlow.value = value
    }

val userPosts: Flow<List<Post>> = userFlow.flatMapLatest { user ->
    if (user == null) emptyFlow() else database.getAllPostsForUser(user)
}

【讨论】:

  • 嗯。似乎可以通过使用户本身成为StateFlow 并使用flatMapLatest 派生userPosts 来简化这一点
  • 感谢您的建议,等我回去工作后,我会尝试将Flow作为方法调用的结果返回。
  • 返回Flow&lt;List&lt;Posts&gt;&gt; 效果很好,再次感谢您,编码愉快:)!
猜你喜欢
  • 2018-12-14
  • 2015-07-31
  • 1970-01-01
  • 1970-01-01
  • 2023-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多