Ich kann LiveData von mehreren Fragmenten aus beobachten. Kann ich das mit Flow machen? Wenn ja, wie dann?
Ja. Sie können dies mit emit
und tun collect
. Think emit
ähnelt Live-Daten postValue
und collect
ähnelt observe
. Geben wir ein Beispiel.
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.getWeatherForecastEveryTwoSeconds().collect {
// Use the weather forecast data
// This will be called 3 times since we have 3
// weather forecast data
}
}
}
Mit map & switchMap können mehrere LiveData aus einer einzigen LiveData stammen. Gibt es eine Möglichkeit, mehrere Flows aus einer einzigen Quelle zu erhalten?
Flow ist sehr praktisch. Sie können einfach einen Fluss innerhalb des Flusses erstellen. Nehmen wir an, Sie möchten jedem Wettervorhersagedaten ein Gradzeichen hinzufügen.
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)
}
}
}
Sammeln Sie dann die Daten in Fragment wie in Nr. 1. Hier sammelt das Ansichtsmodell Daten aus dem Repository und das Fragment Daten aus dem Ansichtsmodell.
Mit MutableLiveData kann ich Daten von überall mithilfe der Variablenreferenz aktualisieren. Gibt es eine Möglichkeit, dasselbe mit Flow zu tun?
Sie können keinen Wert außerhalb des Flusses ausgeben. Der Codeblock innerhalb des Flusses wird nur ausgeführt, wenn ein Kollektor vorhanden ist. Sie können den Datenfluss jedoch mithilfe der asLiveData-Erweiterung von LiveData in Live-Daten konvertieren.
ViewModel
fun getWeatherForecast(): LiveData<String> {
return forecastRepository
.getWeatherForecastEveryTwoSeconds()
.asLiveData() // Convert flow to live data
}
In Ihrem Fall können Sie dies tun
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
}
Bearbeiten
Vielen Dank an @mark für seinen Kommentar. Das Erstellen eines neuen Flusses im Ansichtsmodell für die getWeatherForecast
Funktion ist eigentlich nicht erforderlich. Es könnte umgeschrieben werden als
fun getWeatherForecast(): Flow<String> {
return forecastRepository
.getWeatherForecastEveryTwoSeconds(spendingDetailsRequest)
.map {
it + " °C"
}
}