Ich habe einige Fragen bzgl Kotlin-Flow
- Ich kann beobachten
LiveData
aus mehreren Fragmenten. Kann ich das mit machen Flow
? Wenn ja, wie dann?
- Wir können mehrere haben
LiveData
von einem einzigen LiveData
verwenden map
& switchMap
. Gibt es eine Möglichkeit mehrere zu haben Flow
aus einer Hand Flow
?
- Verwenden
MutableLiveData
Ich kann Daten von überall mit der Variablenreferenz aktualisieren. Gibt es eine Möglichkeit, dasselbe mit zu tun Flow
?
Ich habe einen Anwendungsfall wie: Ich werde a beobachten SharedPreferences
verwenden callbackFlow{...}
was mir einen Single-Source-Flow geben wird. Aus diesem Flow möchte ich mehrere Flows für jedes Schlüssel-Wert-Paar erstellen.
Das mögen dumme Fragen klingen. Ich bin neu in der Rx- und Flow-Welt.
Ich kann LiveData von mehreren Fragmenten beobachten. Kann ich das mit Flow machen? Wenn ja, wie dann?
Ja. Sie können dies mit tun emit
und collect
. Denken emit
ist ähnlich wie Live-Daten postValue
und collect
ist ähnlich wie observe
. Lassen Sie uns ein Beispiel geben.
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
}
}
}
Wir können mehrere LiveData von einem einzigen LiveData haben, indem wir map& switchMap verwenden. Gibt es eine Möglichkeit, mehrere Flows aus einem einzigen Source Flow zu haben?
Flow ist sehr praktisch. Sie können einfach einen Fluss innerhalb eines Flusses erzeugen. Nehmen wir an, Sie möchten Gradzeichen an alle Wettervorhersagedaten anhängen.
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 bei Nr. 1. Hier sammelt das Ansichtsmodell Daten aus dem Repository und das Fragment sammelt Daten aus dem Ansichtsmodell.
Mit MutableLiveData kann ich Daten von überall mit der Variablenreferenz aktualisieren. Gibt es eine Möglichkeit, dasselbe mit Flow zu tun?
Sie können keinen Wert außerhalb des Flusses emittieren. Der Codeblock innerhalb des Flusses wird nur ausgeführt, wenn ein Kollektor vorhanden ist. Sie können den Fluss jedoch in Live-Daten umwandeln, indem Sie die Erweiterung asLiveData von LiveData verwenden.
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
Danke an @mark für seinen Kommentar. Erstellen eines neuen Flows im Ansichtsmodell für getWeatherForecast
Funktion ist eigentlich unnötig. Es könnte umgeschrieben werden als
fun getWeatherForecast(): Flow<String> {
return forecastRepository
.getWeatherForecastEveryTwoSeconds(spendingDetailsRequest)
.map {
it + " °C"
}
}
Da ist ein neues Flow.asLiveData()
Erweiterungsfunktion im neuen androidx.lifecycle
ktx-Pakete. Mehr erfährst du in meinem Artikel:
https://www.netguru.com/codestories/android-coroutines-%EF%B8%8Fin-2020
In einer 3-Tier-Architektur: Datendomänenpräsentation, Flow sollte in der Datenschicht (Datenbanken, Netzwerk, Cache …) stattfinden, und dann, wie Samuel Urbanowicz erwähnt hat, können Sie Flow auf LiveData abbilden.
Im Allgemeinen ist Flow fast das, was Observable (oder Flowable) für RxJava ist. Verwechseln Sie es nicht mit LiveData.
mehr hier: https://medium.com/@elizarov/cold-flows-hot-channels-d74769805f9
Ich wollte Fatihs Antwort hier nur ergänzen, da es irgendwann her ist.
Ich kann LiveData von mehreren Fragmenten beobachten. Kann ich das mit Flow machen? Wenn ja, wie dann?
Ja. Aber die Art und Weise, wie Sie das tun sollten, hat sich ein wenig geändert. Du solltest benutzen repeatOnLifecycle
um sicherer von Flows aus auf der Benutzeroberfläche zu posten. Es ist neu und die Dokumente sind knapp, aber so sieht es aus:
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.getWeatherForecast().collect {
// Safely update the UI
}
}
}
Dadurch wird sichergestellt, dass die Wettervorhersage die Benutzeroberfläche nur aktualisiert, wenn sie angezeigt wird, und keine Ressourcen verschwendet. Und ja, Sie können dies für mehrere Fragmente aus demselben Flow gleichzeitig tun.
Wir können mehrere LiveData von einem einzigen LiveData haben, indem wir map& switchMap verwenden. Gibt es eine Möglichkeit, mehrere Flows aus einem einzigen Source Flow zu haben?
Dies ist ein klares Ja. Flows haben unzählige Operatoren wie map und switchMap
Mit MutableLiveData kann ich Daten von überall mit der Variablenreferenz aktualisieren. Gibt es eine Möglichkeit, dasselbe mit Flow zu tun?
Ja. Wir haben nun MutableStateFlow was dem sehr nahe kommt und mächtiger ist MutableLiveData
.
val textFlow = MutableStateFlow("Hello")
someButton.onPress {
textFlow.value = "World"
}
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
textFlow.collect {
someTextView.text = it
}
}
}
Der obige SharedPreferences-Code kann ein wenig modifiziert werden:
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
}
}
}
}
Für welchen Ansatz haben Sie sich entschieden – Fließen oder Lebensdaten?
– IgorGanapolsky
12. Dezember 2019 um 0:36 Uhr
Derzeit verwende ich LiveData für Ansichten und Flow für alles andere. In ViewModel erhalte ich Flow und gebe LiveData aus, um Fragmente zu beobachten.
– zoha131
12. Dezember 2019 um 5:33 Uhr
@zoha131 du machst es richtig! Da LiveData nur im Hauptthread beobachtet werden können, passen sie perfekt zu View<->ViewModel-Interaktionen. Dann können Sie mit Flows komplexere Operationen im Rest Ihrer Architektur durchführen.
– smora
15. März 2020 um 14:17 Uhr