[英]Kotlin Flow vs Android LiveData
我有一些關於Kotlin Flow的問題
LiveData
。 我可以用Flow
做到這一點嗎? 如果是那么如何?map
和LiveData
從單個LiveData
獲得多個switchMap
。 有什么辦法可以從一個源Flow
獲得多個Flow
嗎?MutableLiveData
我可以使用變量引用從任何地方更新數據。 有什么辦法可以對Flow
做同樣的事情嗎? 我有一個這樣的用例:我將使用callbackFlow{...}
觀察SharedPreferences
,這將給我一個單一的源流。 從該流中,我想為每個鍵值對創建多個流。
這些問題聽起來可能很愚蠢。 我是 Rx 和 Flow 世界的新手。
我可以從多個片段中觀察 LiveData。 我可以用 Flow 做到這一點嗎? 如果是,那怎么辦?
是的。 您可以使用emit
和collect
來做到這一點。 認為emit
類似於實時數據postValue
,而collect
類似於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.getWeatherForecast().collect {
// Use the weather forecast data
// This will be called 3 times since we have 3
// weather forecast data
}
}
}
我們可以使用 map&switchMap 從單個 LiveData 中獲取多個 LiveData。 有沒有辦法從單個源流中獲得多個流?
流量非常好用。 您可以在流中創建流。 假設您要對每個天氣預報數據使用 append 度數符號。
視圖模型
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相同的片段中的數據。 這里發生的是視圖 model 正在從存儲庫中收集數據,片段正在從視圖 model 中收集數據。
使用 MutableLiveData 我可以使用變量引用從任何地方更新數據。 有沒有辦法對 Flow 做同樣的事情?
你不能在流量之外發出價值。 flow里面的代碼塊只有在有collector的時候才會執行。 但是您可以使用 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 的評論。 在視圖 model 中為getWeatherForecast
function 創建一個新流實際上是不必要的。 它可以重寫為
fun getWeatherForecast(): Flow<String> {
return forecastRepository
.getWeatherForecastEveryTwoSeconds(spendingDetailsRequest)
.map {
it + " °C"
}
}
在新的androidx.lifecycle
ktx 包中有一個新的Flow.asLiveData()
擴展 function。 您可以在我的文章中了解更多信息: https://www.netguru.com/codestories/android-coroutines-%EF%B8%8Fin-2020
在 3 層架構中:數據域表示,流應該發生在數據層(數據庫、網絡、緩存......),然后正如Samuel Urbanowicz提到的,您可以將 map 流到 LiveData。
一般來說,Flow 幾乎就是 RxJava 的 Observable(或 Flowable)。 不要將它與 LiveData 混淆。
更多信息: https://medium.com/@elizarov/cold-flows-hot-channels-d74769805f9
只是想在這里補充 Fatih 的回答,因為已經有一段時間了。
我可以從多個片段觀察 LiveData。 我可以用 Flow 做到這一點嗎? 如果是那么如何?
是的。 但是您應該這樣做的方式有所改變。 您應該使用repeatOnLifecycle
來更安全地從 Flows 發布到 UI。 它是新的,文檔很少,但這就是它的樣子:
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.getWeatherForecast().collect {
// Safely update the UI
}
}
}
這可確保天氣預報僅在顯示時更新 UI,而不會浪費資源。 是的,您可以同時對來自同一流的多個片段執行此操作。
我們可以使用 map 和 switchMap 從單個 LiveData 中獲得多個 LiveData。 有什么辦法可以從一個源流中獲得多個流?
這是一個明顯的肯定。 流有大量的運算符,如 map 和 switchMap
使用 MutableLiveData 我可以使用變量引用從任何地方更新數據。 有什么辦法可以對 Flow 做同樣的事情嗎?
是的。 我們現在有了MutableStateFlow ,它非常接近並且比MutableLiveData
更強大。
val textFlow = MutableStateFlow("Hello")
someButton.onPress {
textFlow.value = "World"
}
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
textFlow.collect {
someTextView.text = it
}
}
}
上面的 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
}
}
}
}
對於您的用例 - 您可以這樣做:
''' yourFlow.filter(...) - 檢查鍵 value.onEach(...) - 做你的神奇 logic.launchIn(viewModelScope) - 或任何地方 ''' 對不起,糟糕的格式,我從電話。
希望這可以幫助。 干杯!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.