如何获取协程外Flow的值?

How to get the value of a Flow outside a coroutine?

如何像 LiveData 一样在协程外获取 Flow 的值?

// 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.

中的身份验证目的很有用

嗯...您要找的并不是 Flow 真正要找的。 Flow 只是一个流。它不是一个价值持有者,所以你没有任何东西可以取回。

因此,根据您的拦截器的需要,有两种主要途径。

也许您的拦截器可以在没有来自存储库的数据的情况下生存。 IOW,如果数据存在,您将使用它,否则拦截器可以继续。在这种情况下,您可以让您的存储库发出流,但也可以维护您的拦截器可以使用的 "current value" 缓存。这可能是通过:

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

如果您的拦截器需要数据,那么 none 将直接工作,因为如果数据尚未准备好,它们都会导致拦截器获得 null。您需要的是 can 阻塞的调用,但如果数据已通过某种形式的缓存准备就绪,则可能会快速评估。根据存储库的实现以及首先提供 Flow 的内容,其细节会有很大差异。

你可以做到这一点

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

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

但并不总是可以让所有事情都异步,就此而言,甚至 'just make a synchronous method' 也不总是可能的。例如,当前的 Datastore 版本(应该取代 Android 上的共享首选项)只公开 Flow 而没有其他内容。这意味着您很容易陷入这种情况,因为 Activity 或 Fragment 的生命周期方法中有 none 是协程。

如果可以的话,您应该始终从挂起函数调用协程并避免进行 runBlocking 调用。很多时候它是这样工作的。但这并不是一种万无一失的方法。您可以使用 runBlocking.

引入死锁

你可以使用这样的东西:

    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
    }

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

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