Kotlin Flow与Android LiveData


20

我对Kotlin Flow有一些疑问

  1. 我可以从多个片段观察LiveData。我可以用Flow做到这一点吗?如果是,那怎么办?
  2. 我们可以使用map&从单个LiveData获得多个LiveData switchMap。有没有办法从一个源流获得多个流?
  3. 使用,MutableLiveData我可以使用变量引用从任何地方更新数据。有什么办法可以对Flow进行同样的操作?

我有一个用例这样的:我会观察SharedPreferences使用callbackFlow{...},这将给我一个单一源流。从该流中,我想为每个键值对创建多个流。

这些听起来很愚蠢。我是Rx and Flow世界的新手。


哪种方法你看中- 流量LiveData
IgorGanapolsky

2
目前,我正在使用LiveData查看数据,使用Flow处理其他所有数据。在ViewModel中,我接收Flow并发出LiveData以从片段进行观察。
zoha131

@ zoha131您以正确的方式做到了!由于只能在主线程上观察LiveData,因此它们非常适合View <-> ViewModel交互。然后,Flows使您可以在架构的其余部分中进行更复杂的操作。
smora

Answers:


15

我可以从多个片段观察LiveData。我可以用Flow做到这一点吗?如果是,那怎么办?

是。您可以使用emit和进行此操作collect。Think emit与实时数据相似,postValuecollect相似observe。让我们举个例子。

资料库

// I just faked the weather forecast
val weatherForecast = listOf("10", "12", "9")

// This function returns flow of forecast data
// Whenever the data is fetched, it is emitted so that
// collector can collect (if there is any)
fun getWeatherForecastEveryTwoSeconds(): Flow<String> = flow { 
    for (i in weatherForecast) {
        delay(2000)
        emit(i)
    }
}

视图模型

fun getWeatherForecast(): Flow<String> {
    return forecastRepository.getWeatherForecastEveryTwoSeconds()
}

分段

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    // Collect is suspend function. So you have to call it from a 
    // coroutine scope. You can create a new coroutine or just use 
    // lifecycleScope
    // https://developer.android.com/topic/libraries/architecture/coroutines
    lifecycleScope.launch {
            viewModel.getWeatherForecastEveryTwoSeconds().collect {
                    // Use the weather forecast data
                    // This will be called 3 times since we have 3 
                    // weather forecast data
            }
    }
}

我们可以使用map&switchMap从单个LiveData获得多个LiveData。有没有办法从一个源流获得多个流?

流程非常方便。您可以在流内部创建流。假设您要将度数符号附加到每个天气预报数据中。

视图模型

fun getWeatherForecast(): Flow<String> {
    return flow {
        forecastRepository
            .getWeatherForecastEveryTwoSeconds(spendingDetailsRequest)
                .map {
                    it + " °C"
                }
                .collect {
                    // This will send "10 °C", "12 °C" and "9 °C" respectively
                    emit(it) 
                }
    }
}

然后在与#1相同的Fragment中收集数据。这里发生的是视图模型从存储库收集数据,而片段从视图模型收集数据。

使用MutableLiveData,我可以使用变量引用从任何地方更新数据。有什么办法可以对Flow进行同样的操作?

您不能在流程之外释放价值。内部流的代码块仅在有任何收集器时才执行。但是您可以使用LiveData的asLiveData扩展将流转换为实时数据。

视图模型

fun getWeatherForecast(): LiveData<String> {
    return forecastRepository
    .getWeatherForecastEveryTwoSeconds()
    .asLiveData() // Convert flow to live data
}

您可以这样做

private fun getSharedPrefFlow() = callbackFlow {
    val sharedPref = context?.getSharedPreferences("SHARED_PREF_NAME", MODE_PRIVATE)
    sharedPref?.all?.forEach {
        offer(it)
    }
}

getSharedPrefFlow().collect {
    val key = it.key
    val value = it.value
}

编辑

感谢@mark的评论。getWeatherForecast实际上,不需要在视图模型中为功能创建新的流程。它可以重写为

fun getWeatherForecast(): Flow<String> {
        return forecastRepository
                .getWeatherForecastEveryTwoSeconds(spendingDetailsRequest)
                    .map {
                        it + " °C"
                    }
    }


我不知道为什么,但是我有一个假设,我不能为单个Flow在多个地方调用collect()函数。感谢您的回答。
zoha131 '19

1
不可以。您可以在多个地方收集相同的流。val sharedPref = getSharedPref()您可以在多个地方使用collect sharedPref.collect {}。唯一的原因是collect处于挂起状态,因此您需要从协程块中调用它。很高兴帮助np :)
法提赫(Fatih)

对于我的第三个问题,解决方法可能是广播频道。
zoha131

您可以检查此提交是否使用通道而不是实时数据。github.com/android/plaid/pull/770/commits/…–
法提赫(Fatih)

1
是的,你是对的。这就是流入的地方。通道有很多事情需要您处理,它们很热,这意味着即使没有观察者,它们也总是打开的。但是,有了流程,您会获得相同的好处而无需担心,因为它们很冷。因此,我认为最好使用流量代替渠道
法提赫(Fatih)

3

Flow.asLiveData()新的androidx.lifecyclektx软件包中有一个新的扩展功能。您可以在我的文章中了解更多信息:https : //www.netguru.com/codestories/android-coroutines-%EF%B8%8Fin-2020


我们什么时候需要使用它?
IgorGanapolsky

1
当您想满足需要LiveData和Flow实例的API时
Samuel Urbanowicz

据谷歌,我们必须选择要么 LiveData 流量:codelabs.developers.google.com/codelabs/...
IgorGanapolsky

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.