简体   繁体   English

如何在协程之外获取 Flow 的值?

[英]How to get the value of a Flow outside a coroutine?

How can I get the value of a Flow outside a coroutine similarly to LiveData?与 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

Context语境

I'm avoiding LiveData in the repository layer in favor of Flow .我避免在存储库层中使用LiveData以支持Flow Yet, I need to set, observe and collect the value for immediate consumption.然而,我需要设置、观察和收集立即消费的价值。 The later is useful for authentication purpose in a OkHttp3 Interceptor .后者可用于 OkHttp3 Interceptor中的身份验证目的。

You can do this你可以这样做

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

Yes its not exactly what Flow was made for.是的,它不完全是 Flow 的用途。

But its not always possible to make everything asynchronous and for that matter it may not even always be possible to 'just make a synchronous method'.但它并不总是可以使一切都异步,就此而言,甚至可能并不总是可以“只制作一个同步方法”。 For instance the current Datastore releases (that are supposed to replace shared preferences on Android) do only expose Flow and nothing else.例如,当前的 Datastore 版本(应该替换 Android 上的共享首选项)只公开 Flow 而没有其他任何内容。 Which means that you will very easiely get into such a situation, given that none of the Lifecycle methods of Activities or Fragments are coroutines.这意味着您将很容易陷入这种情况,因为活动或片段的生命周期方法都不是协程。

If you can help it you should always call coroutines from suspend functions and avoid making runBlocking calls.如果您能提供帮助,您应该始终从挂起函数调用协程,并避免进行runBlocking调用。 A lot of the time it works like this.很多时候它是这样工作的。 But it´s not a surefire way that works all the time.但这并不是始终有效的万无一失的方式。 You can introduce deadlocks with runBlocking .您可以使用runBlocking引入死锁。

Well... what you're looking for isn't really what Flow is for.嗯...您正在寻找的并不是Flow的真正用途。 Flow is just a stream. Flow只是一个 stream。 It is not a value holder, so there is nothing for you retrieve.它不是价值持有者,因此您无法检索任何内容。

So, there are two major avenues to go down, depending on what your interceptor needs.因此,根据您的拦截器需要,有两种主要途径可以降低 go。

Perhaps your interceptor can live without the data from the repository.也许您的拦截器可以在没有来自存储库的数据的情况下生存。 IOW, you'll use the data if it exists, but otherwise the interceptor can continue along. IOW,如果数据存在,您将使用该数据,否则拦截器可以继续。 In that case, you can have your repository emit a stream but also maintain a "current value" cache that your interceptor can use.在这种情况下,您可以让您的存储库发出 stream ,但还可以维护您的拦截器可以使用的“当前值”缓存。 That could be via:这可以通过:

  • BroadcastChannel
  • LiveData
  • a simple property in the repository that you update internally and expose as a val存储库中的一个简单属性,您在内部更新并公开为val

If your interceptor needs the data, though, then none of those will work directly, because they will all result in the interceptor getting null if the data is not yet ready.但是,如果您的拦截器需要数据,那么这些都不会直接工作,因为如果数据尚未准备好,它们都会导致拦截器获取null What you would need is a call that can block, but perhaps evaluates quickly if the data is ready via some form of cache.您需要的是一个可以阻塞的调用,但如果数据通过某种形式的缓存准备好,可能会快速评估。 The details of that will vary a lot based on the implementation of the repository and what is supplying the Flow in the first place.根据存储库的实现以及首先提供Flow的内容,其细节会有很大差异。

You should look into StateFlow , where you get access to the value.您应该查看StateFlow ,您可以在其中访问该值。

https://developer.android.com/kotlin/flow/stateflow-and-sharedflow https://developer.android.com/kotlin/flow/stateflow-and-sharedflow

You could use something like this:你可以使用这样的东西:

    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
    }

You can use MutableStateFlow and MutableSharedFlow for emitting the data from coroutine and receiving the data inside Activity/Fragment .您可以使用MutableStateFlowMutableSharedFlow从协程发出数据并在Activity/Fragment中接收数据。 MutableStateFlow can be used for state management. MutableStateFlow可用于 state 管理。 It required default value when initialised.初始化时需要默认值。 Whereas MutableSharedFlow does not need any default value.MutableSharedFlow不需要任何默认值。

But, if you don't want to receive stream of data, (ie) your API call sends data only once, you can use suspend function inside coroutine scope and the function will perform the task and return the result like synchronous function call. But, if you don't want to receive stream of data, (ie) your API call sends data only once, you can use suspend function inside coroutine scope and the function will perform the task and return the result like synchronous function call.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM