Kotlin Flow vs. Android LiveData

Lesezeit: 7 Minuten

Benutzer-Avatar
zoha131

Ich habe einige Fragen bzgl Kotlin-Flow

  1. Ich kann beobachten LiveData aus mehreren Fragmenten. Kann ich das mit machen Flow? Wenn ja, wie dann?
  2. 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?
  3. 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.

  • 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

Benutzer-Avatar
Fatih

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"
                    }
    }

  • Ich weiß nicht warum, aber ich hatte die Vermutung, dass ich die Funktion collect () nicht an mehreren Stellen für einen einzelnen Flow aufrufen kann. Danke für die Antwort.

    – zoha131

    9. November 2019 um 7:07 Uhr

  • Nein. Sie können denselben Flow an mehreren Stellen sammeln. val sharedPref = getSharedPref()und Sie können Collect an mehreren Stellen verwenden sharedPref.collect {}. Die einzige Sache ist, weil collect suspendiert ist, müssen Sie es vom Coroutine-Block aufrufen. Und helfe gerne np 🙂

    – Fatih

    9. November 2019 um 10:01 Uhr

  • Für meine dritte Frage könnte die Problemumgehung ein Broadcast-Kanal sein.

    – zoha131

    9. November 2019 um 10:45 Uhr

  • @Faith Google empfiehlt, sich von Channels API fernzuhalten, es sei denn really necessary

    – IgorGanapolsky

    12. Dezember 2019 um 0:42 Uhr

  • Ja, du hast recht. Hier kommt der Flow ins Spiel. Kanäle haben viele Dinge, um die man sich kümmern muss, und sie sind heiß, was bedeutet, dass sie immer offen sind, auch wenn es keine Beobachter gibt. Aber mit Flow können Sie die gleichen Vorteile ohne Bedenken erhalten, weil sie kalt sind. Anstelle von Kanal denke ich, dass es besser ist, Flow zu verwenden

    – Fatih

    13. Dezember 2019 um 7:02 Uhr

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

  • Wann müssen wir das verwenden?

    – IgorGanapolsky

    18. Februar 2020 um 21:37 Uhr

  • Wenn Sie eine API erfüllen möchten, die LiveData mit einer Flow-Instanz erfordert

    – Samuel Urbanowicz

    19. Februar 2020 um 20:23 Uhr

  • Wenn Sie eine Raumdatenbank haben und alle Änderungen in Ihrer Datenbank beobachten möchten, aber weil Sie eine saubere Architektur verwenden, sollte Ihr Repository keine Live-Daten zurückgeben – da es plattformbezogen ist – als saubere Lösung sollten Ihre Doa-Klassen auch den Fluss zurückgeben Ihr Repository sollte den Fluss zurückgeben und sie dann wie in der Antwort gezeigt in Livedaten umwandeln. Stellen Sie sicher, dass dieselbe doa-Instanz zum Abrufen und Aktualisieren von Daten verwendet wird, damit der Fluss ordnungsgemäß funktioniert

    – Amr

    15. März 2021 um 11:34 Uhr

  • @Amr Gibt es einen Grund, es nicht zu bevorzugen lifecycleScope.launch { flow.collect() { [...] } } (mit Ausnahme der ‘Boilerplatiness’), um die zusätzliche Abhängigkeit der Verwendung von LiveData zu vermeiden?

    – Filip Östermark

    18. März um 9:59 Uhr

  • @FilipÖstermark Tatsächlich ersetzen jetzt alle LiveData durch Flow. Hier finden Sie weitere Informationen Techniker untergehen LiveData ist enthalten —————- Guy von Google schreibt, wie man von LiveData zu Flow migriert —————- Hier, wie man den Fluss normal sammelt

    – Amr

    20. März um 6:49 Uhr


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

  • Für One-Shot-Operationen (dh Datenbanklesen) ist LiveData ausreichend.

    – IgorGanapolsky

    18. Februar 2020 um 21:38 Uhr

  • “Verwechseln Sie es nicht mit LiveData.” Wie? Ich bin neu bei Kotin Flow und habe bisher verstanden, dass die gleiche Funktionalität auch mit LiveData erreicht werden kann. Können Sie das bitte im Detail erklären. Danke im Voraus

    – Astha Garg

    4. Juni 2021 um 14:49 Uhr


  • @AsthaGarg, wie die Dokumentation besagt, ist LiveData ein lebenszyklusbewusstes Observable und wird hauptsächlich zwischen ViewModel und Fragments/Activities verwendet. Natürlich können Sie LiveData verwenden, um beispielsweise Updates von Datenbanken zu erhalten, aber aus diesem Grund sollten Sie jetzt Flow verwenden. Diese Seite developer.android.com/kotlin/flow erklärt es besser als ich es kann. Und denken Sie daran, dass StateFlow/SharedFlow jetzt die neuen LiveData sind

    – gts13

    9. Juni 2021 um 15:41 Uhr

Benutzer-Avatar
Ajmal Kunnummal

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
                }
        }
    }
}

1282800cookie-checkKotlin Flow vs. Android LiveData

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy