kotlin 如何在协程之外获得流的值?

tzdcorbm  于 2023-02-09  发布在  Kotlin
关注(0)|答案(5)|浏览(183)

如何在协同程序之外获得类似于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中的身份验证非常有用。

zbsbpyhn

zbsbpyhn1#

你能做到的

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

是的,这不完全是流量的目的。
但是并不总是可以使所有的东西都异步,就这一点而言,甚至不总是可以"只创建一个同步方法"。(应该取代Android上的共享首选项)只暴露Flow,不暴露其他任何东西。这意味着你将非常容易陷入这样的情况,假设活动或片段的生命周期方法都不是协程。
如果你能帮上忙,你应该总是从suspend函数调用协程,避免调用runBlocking。很多时候它是这样工作的。但这不是一个总是有效的万无一失的方法。你可以用runBlocking引入死锁。

pbpqsu0x

pbpqsu0x2#

好吧......你要找的东西并不是Flow真正要找的东西。Flow只是一个流,它不是一个值保持器,所以没有什么东西可供你检索。
因此,根据拦截器的需要,有两种主要的方法可以使用。
也许你的拦截器可以在没有仓库数据的情况下运行。如果数据存在,你就使用它,否则拦截器可以继续沿着。在这种情况下,你可以让仓库发出一个流,但同时维护一个拦截器可以使用的“当前值”缓存。这可以通过以下方式实现:

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

但是如果拦截器需要数据,那么这些方法都不会直接起作用,因为如果数据还没有准备好,它们都会导致拦截器得到null。您需要的是一个 can block的调用。但是如果数据经由某种形式的高速缓存准备好,则可能快速地评估。根据存储库的实现以及最初提供Flow的内容,具体细节会有很大不同。

kq0g1dla

kq0g1dla3#

您可以使用以下内容:

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
    }
uurv41yg

uurv41yg4#

可以使用MutableStateFlowMutableSharedFlow从协程中发送数据和在Activity/Fragment中接收数据。MutableStateFlow可以用于状态管理。初始化时需要默认值。而MutableSharedFlow不需要任何默认值。
但是,如果您不想接收数据流,(即)您的API调用只发送数据一次,您可以在协程范围内使用suspend函数,该函数将执行任务并像同步函数调用一样返回结果。

rnmwe5a2

rnmwe5a25#

要在协同程序之外获取Flow的值,最好的选择是将流创建为StateFlow,然后调用StateFlow上的value属性。

class MyClass {
   private val mutableProperty = MutableStateFlow(1)
   val property = mutableProperty.asStateFlow()

   ...
   mutableProperty.value = 2
}
...

val readProperty = MyClass().property.value
val propertyAsFlow = MyClass().property as Flow<Int>

相关问题