簡體   English   中英

Kotlin Flow 與 Android LiveData

[英]Kotlin Flow vs Android LiveData

我有一些關於Kotlin Flow的問題

  1. 我可以從多個片段觀察LiveData 我可以用Flow做到這一點嗎? 如果是那么如何?
  2. 我們可以使用mapLiveData從單個LiveData獲得多個switchMap 有什么辦法可以從一個源Flow獲得多個Flow嗎?
  3. 使用MutableLiveData我可以使用變量引用從任何地方更新數據。 有什么辦法可以對Flow做同樣的事情嗎?

我有一個這樣的用例:我將使用callbackFlow{...}觀察SharedPreferences ,這將給我一個單一的源流。 從該流中,我想為每個鍵值對創建多個流。

這些問題聽起來可能很愚蠢。 我是 Rx 和 Flow 世界的新手。

我可以從多個片段中觀察 LiveData。 我可以用 Flow 做到這一點嗎? 如果是,那怎么辦?

是的。 您可以使用emitcollect來做到這一點。 認為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
                }
        }
    }
}
  1. 當然可以。 您只需使用 launchIn(scope)、onEach() 等的組合在任何您想要的地方啟動它。
  2. 有很多用於流的運算符/轉換。 自己寫一個很容易 - 只需查看源代碼並嘗試:)
  3. 您可能可以通過擁有一個私人廣播頻道然后使用consumeAsFlow(如果我沒記錯的話)使用它來解決它

對於您的用例 - 您可以這樣做:

''' yourFlow.filter(...) - 檢查鍵 value.onEach(...) - 做你的神奇 logic.launchIn(viewModelScope) - 或任何地方 ''' 對不起,糟糕的格式,我從電話。

希望這可以幫助。 干杯!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM