【问题标题】:How to get the value of a Flow outside a coroutine?如何在协程之外获取 Flow 的值?
【发布时间】:2020-04-13 20:56:57
【问题描述】:

如何在协程之外获取 Flow 的值,类似于 LiveData?

// Suspend function 'first' should be called only from a coroutine or another suspend function
flowOf(1).first()
// value is null
flowOf(1).asLiveData().value
// works
MutableLiveData(1).value

上下文

我在存储库层中避免使用LiveData,而使用Flow。然而,我需要设置、观察和收集立即消费的价值。后者可用于 OkHttp3 Interceptor 中的身份验证。

【问题讨论】:

  • asLiveData 创建一个 LiveData,它启动自己的协程以从 Flow 中收集值。在上面的示例中,value 的初始值将是 null,因为协程还没有机会启动。
  • Flow 的重点是使用它来获取太耗时而无法同步执行的数据,因此如果没有协程,您永远不会希望从中获取值。
  • @Tenfour04 确实为空。正如 CommonsWare 在许多答案中指出的那样,我没有从存储库层公开 LiveData。但我需要可观察性。
  • 没有“流的价值”。 LiveData 是一个价值持有者,因此它在任何时间点都有一个价值(或null)。 Flow 只是一个流。 BroadcastChannel 是具有可观察性的价值持有者的协程等价物,尽管它仍被标记为实验性 API IIRC。
  • 如果您的拦截器可以处理null,您可以使用LiveDataBroadcastChannel,或者只是让存储库保留最后收到的值的缓存并通过单独的函数返回它。如果你的拦截器无法处理null,那么你需要向你的拦截器可以使用的存储库添加一个单独的阻塞API,或者尝试使用first()runBlocking()中使用Flow来强制同步行为。跨度>

标签: android kotlin kotlin-flow


【解决方案1】:

你可以这样做

val flowValue: SomeType
runBlocking(Dispatchers.IO) {
    flowValue = myFlow.first()
}

是的,它不完全是 Flow 的用途。

但并非总是可以使所有内容都异步,因此甚至可能不总是可以“只创建一个同步方法”。例如,当前的 Datastore 版本(应该替换 Android 上的共享首选项)只公开 Flow 而没有其他内容。这意味着您很容易陷入这种情况,因为活动或片段的生命周期方法都不是协程。

如果您能提供帮助,您应该始终从挂起函数调用协程,并避免进行 runBlocking 调用。很多时候它是这样工作的。但这并不是始终有效的万无一失的方式。您可以使用runBlocking 引入死锁。

【讨论】:

  • 感谢您的警告。此外,runBlocking 可能会阻塞 UI 主线程,应尽可能避免。
【解决方案2】:

嗯...您正在寻找的并不是Flow 的真正用途。 Flow 只是一个流。它不是价值持有者,因此您无法检索任何内容。

因此,根据拦截器的需求,有两种主要途径可供选择。

也许您的拦截器可以在没有来自存储库的数据的情况下生存。 IOW,如果数据存在,您将使用该数据,否则拦截器可以继续。在这种情况下,您可以让您的存储库发出一个流,但也可以维护您的拦截器可以使用的“当前值”缓存。可以通过:

  • BroadcastChannel
  • LiveData
  • 您在内部更新并公开为val 的存储库中的一个简单属性

如果你的拦截器需要数据,那么这些都不会直接工作,因为如果数据还没有准备好,它们都会导致拦截器得到null。你需要的是一个 can 阻塞的调用,但如果数据通过某种形式的缓存准备好,可能会快速评估。具体细节会根据存储库的实现以及首先提供 Flow 的内容而有很大差异。

【讨论】:

  • Room 首先提供Flow。我使用 Room 作为当前用户和令牌的持久存储。
  • @maxbeaudoin:好的,那么,最粗略的实现是添加一个单独的阻塞 DAO 函数,您的存储库依次公开,拦截器使用该函数。
  • 谢谢。我有很多事情要考虑。
【解决方案3】:

您可以使用MutableStateFlowMutableSharedFlow 从协程发出数据并接收Activity/Fragment 内部的数据。 MutableStateFlow 可用于状态管理。初始化时需要默认值。而MutableSharedFlow 不需要任何默认值。

但是,如果您不想接收数据流,(即)您的 API 调用只发送一次数据,您可以在协程范围内使用挂起函数,该函数将执行任务并像同步函数一样返回结果打电话。

【讨论】:

    【解决方案4】:

    你可以这样使用:

        fun <T> SharedFlow<T>.getValueBlockedOrNull(): T? {
            var value: T?
            runBlocking(Dispatchers.Default) {
                value = when (this@getValueBlockedOrNull.replayCache.isEmpty()) {
                    true -> null
                    else -> this@getValueBlockedOrNull.firstOrNull()
                }
            }
            return value
        }
    

    【讨论】:

      猜你喜欢
      • 2020-10-29
      • 1970-01-01
      • 1970-01-01
      • 2021-07-14
      • 2021-12-16
      • 2018-09-17
      • 1970-01-01
      • 2021-03-16
      • 2022-01-23
      相关资源
      最近更新 更多