[英]Kotlin Flow vs Android LiveData
I have some questions about Kotlin Flow我有一些关于Kotlin Flow的问题
LiveData
from multiple Fragments.LiveData
。 Can I do this with Flow
?Flow
做到这一点吗? If yes then how?LiveData
from a single LiveData
using map
& switchMap
.map
和LiveData
从单个LiveData
获得多个switchMap
。 Is there any way to have multiple Flow
from a single source Flow
?Flow
获得多个Flow
吗?MutableLiveData
I can update data from anywhere using the variable reference.MutableLiveData
我可以使用变量引用从任何地方更新数据。 Is there any way to do the same with Flow
?Flow
做同样的事情吗? I have a use-case like: I will observe a SharedPreferences
using callbackFlow{...}
which will give me a single source Flow.我有一个这样的用例:我将使用
callbackFlow{...}
观察SharedPreferences
,这将给我一个单一的源流。 From that Flow, I want to create multiple Flow for each key-value pair.从该流中,我想为每个键值对创建多个流。
These might sound silly questions.这些问题听起来可能很愚蠢。 I am new to Rx and Flow world.
我是 Rx 和 Flow 世界的新手。
I can observe LiveData from multiple Fragments.
我可以从多个片段中观察 LiveData。 Can I do this with Flow?
我可以用 Flow 做到这一点吗? If yes then how?
如果是,那怎么办?
Yes.是的。 You can do this with
emit
and collect
.您可以使用
emit
和collect
来做到这一点。 Think emit
is similar to live data postValue
and collect
is similar to observe
.认为
emit
类似于实时数据postValue
,而collect
类似于observe
。 Lets give an example.让我们举个例子。
Repository存储库
// 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)
}
}
ViewModel视图模型
fun getWeatherForecast(): Flow<String> {
return forecastRepository.getWeatherForecastEveryTwoSeconds()
}
Fragment分段
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.getWeatherForecast().collect {
// Use the weather forecast data
// This will be called 3 times since we have 3
// weather forecast data
}
}
}
We can have multiple LiveData from a single LiveData using map& switchMap.
我们可以使用 map&switchMap 从单个 LiveData 中获取多个 LiveData。 Is there any way to have multiple Flow from a single source Flow?
有没有办法从单个源流中获得多个流?
Flow is very handy.流量非常好用。 You can just create flow inside flow.
您可以在流中创建流。 Lets say you want to append degree sign to each of the weather forecast data.
假设您要对每个天气预报数据使用 append 度数符号。
ViewModel视图模型
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)
}
}
}
Then collect the data in Fragment same as #1.然后收集与#1相同的片段中的数据。 Here what happens is view model is collecting data from repository and fragment is collecting data from view model.
这里发生的是视图 model 正在从存储库中收集数据,片段正在从视图 model 中收集数据。
Using MutableLiveData I can update data from anywhere using the variable reference.
使用 MutableLiveData 我可以使用变量引用从任何地方更新数据。 Is there any way to do the same with Flow?
有没有办法对 Flow 做同样的事情?
You cant emit value outside of flow.你不能在流量之外发出价值。 The code block inside flow is only executed when there is any collector.
flow里面的代码块只有在有collector的时候才会执行。 But you can convert flow to live data by using asLiveData extension from LiveData.
但是您可以使用 LiveData 中的 asLiveData 扩展将流转换为实时数据。
ViewModel视图模型
fun getWeatherForecast(): LiveData<String> {
return forecastRepository
.getWeatherForecastEveryTwoSeconds()
.asLiveData() // Convert flow to live data
}
In your case you can do this在你的情况下,你可以这样做
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
}
Edit编辑
Thanks to @mark for his comment.感谢@mark 的评论。 Creating a new flow in the view model for
getWeatherForecast
function is actually unnecessary.在视图 model 中为
getWeatherForecast
function 创建一个新流实际上是不必要的。 It could be re-written as它可以重写为
fun getWeatherForecast(): Flow<String> {
return forecastRepository
.getWeatherForecastEveryTwoSeconds(spendingDetailsRequest)
.map {
it + " °C"
}
}
There is a new Flow.asLiveData()
extension function in the new androidx.lifecycle
ktx packages.在新的
androidx.lifecycle
ktx 包中有一个新的Flow.asLiveData()
扩展 function。 You can learn more in my article: https://www.netguru.com/codestories/android-coroutines-%EF%B8%8Fin-2020您可以在我的文章中了解更多信息: https://www.netguru.com/codestories/android-coroutines-%EF%B8%8Fin-2020
In a 3-tier architecture: data-domain-presentation, Flow should take place in the data layer (databases, network, cache...) and then as Samuel Urbanowicz mentioned you can map Flow to LiveData.在 3 层架构中:数据域表示,流应该发生在数据层(数据库、网络、缓存......),然后正如Samuel Urbanowicz提到的,您可以将 map 流到 LiveData。
In general, Flow is almost what the Observable (or Flowable) is for RxJava.一般来说,Flow 几乎就是 RxJava 的 Observable(或 Flowable)。 Don't confuse it with LiveData.
不要将它与 LiveData 混淆。
more here: https://medium.com/@elizarov/cold-flows-hot-channels-d74769805f9更多信息: https://medium.com/@elizarov/cold-flows-hot-channels-d74769805f9
Just wanted to add on to Fatih's answer here since it's been sometime.只是想在这里补充 Fatih 的回答,因为已经有一段时间了。
I can observe LiveData from multiple Fragments.
我可以从多个片段观察 LiveData。 Can I do this with Flow?
我可以用 Flow 做到这一点吗? If yes then how?
如果是那么如何?
Yes.是的。 But the way you should do that has changed a bit.
但是您应该这样做的方式有所改变。 You should use
repeatOnLifecycle
to more safely post to the UI from Flows.您应该使用
repeatOnLifecycle
来更安全地从 Flows 发布到 UI。 It's new and the docs are scarce but this is what it looks like:它是新的,文档很少,但这就是它的样子:
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.getWeatherForecast().collect {
// Safely update the UI
}
}
}
This ensures that the weather forecast only updates the UI when it's showing and doesn't waste resources.这可确保天气预报仅在显示时更新 UI,而不会浪费资源。 And yes, you can do this on multiple Fragments from the same Flow at the same time.
是的,您可以同时对来自同一流的多个片段执行此操作。
We can have multiple LiveData from a single LiveData using map& switchMap.
我们可以使用 map 和 switchMap 从单个 LiveData 中获得多个 LiveData。 Is there any way to have multiple Flow from a single source Flow?
有什么办法可以从一个源流中获得多个流?
This is an obvious yes.这是一个明显的肯定。 Flows have tons of operators like map and switchMap
流有大量的运算符,如 map 和 switchMap
Using MutableLiveData I can update data from anywhere using the variable reference.
使用 MutableLiveData 我可以使用变量引用从任何地方更新数据。 Is there any way to do the same with Flow?
有什么办法可以对 Flow 做同样的事情吗?
Yes.是的。 We now have MutableStateFlow which is very close to and more powerful that
MutableLiveData
.我们现在有了MutableStateFlow ,它非常接近并且比
MutableLiveData
更强大。
val textFlow = MutableStateFlow("Hello")
someButton.onPress {
textFlow.value = "World"
}
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
textFlow.collect {
someTextView.text = it
}
}
}
The SharedPreferences code above can be modified a bit:上面的 SharedPreferences 代码可以稍微修改一下:
private fun getSharedPrefFlow() = callbackFlow {
val sharedPref = context.getSharedPreferences("SHARED_PREF_NAME", MODE_PRIVATE)
sharedPref?.all?.forEach {
trySend(it) // offer is deprecated
}
}
init {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
getSharedPrefFlow()
.filter { it.key == BIRTHDAY_KEY }
.collect {
birthdayTextView.text = it.value
}
}
}
}
For your usecase - you could do:对于您的用例 - 您可以这样做:
''' yourFlow.filter(...) - check for key value.onEach(...) - do your magical logic.launchIn(viewModelScope) - or wherever ''' Sorry for the terrible formatting, I'm writing from phone. ''' yourFlow.filter(...) - 检查键 value.onEach(...) - 做你的神奇 logic.launchIn(viewModelScope) - 或任何地方 ''' 对不起,糟糕的格式,我从电话。
Hope this helps.希望这可以帮助。 Cheers!
干杯!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.