Kotlin Flow gegen Android LiveData

20

Ich habe einige Fragen zu Kotlin Flow

  1. Ich kann LiveData von mehreren Fragmenten aus beobachten. Kann ich das mit Flow machen? Wenn ja, wie dann?
  2. Mit map& können wir mehrere LiveData von einer einzigen LiveData haben switchMap. Gibt es eine Möglichkeit, mehrere Flows aus einer einzigen Quelle zu erhalten?
  3. Mit MutableLiveDataIch kann Daten von überall mithilfe der Variablenreferenz aktualisieren. Gibt es eine Möglichkeit, dasselbe mit Flow zu tun?

Ich habe einen Anwendungsfall wie: Ich werde eine SharedPreferencesVerwendung beobachten , callbackFlow{...}die mir einen einzigen Quellfluss gibt. Aus diesem Flow möchte ich für jedes Schlüssel-Wert-Paar mehrere Flows erstellen.

Das klingen vielleicht albern. Ich bin neu in der Rx- und Flow-Welt.

zoha131
quelle
Welchen Ansatz haben Sie gewählt - Flow oder LiveData ?
IgorGanapolsky
2
Derzeit verwende ich LiveData für Ansichten und Flow für alles andere. In ViewModel erhalte ich Flow und sende LiveData aus, um sie aus Fragmenten zu beobachten.
Zoha131
@ zoha131 du machst es richtig! Da LiveData nur im Hauptthread beobachtet werden kann, passen sie perfekt zu View <-> ViewModel-Interaktionen. Mit Flows können Sie dann komplexere Vorgänge im Rest Ihrer Architektur ausführen.
Smora

Antworten:

15

Ich kann LiveData von mehreren Fragmenten aus beobachten. Kann ich das mit Flow machen? Wenn ja, wie dann?

Ja. Sie können dies mit emitund tun collect. Think emitähnelt Live-Daten postValueund 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 getWeatherForecastFunktion ist eigentlich nicht erforderlich. Es könnte umgeschrieben werden als

fun getWeatherForecast(): Flow<String> {
        return forecastRepository
                .getWeatherForecastEveryTwoSeconds(spendingDetailsRequest)
                    .map {
                        it + " °C"
                    }
    }
Fatih
quelle
Ich weiß nicht warum, aber ich ging davon aus, dass ich die Funktion collect () nicht an mehreren Stellen für einen einzelnen Flow aufrufen kann. Danke für die Antwort.
Zoha131
1
Nein. Sie können denselben Fluss an mehreren Stellen sammeln. val sharedPref = getSharedPref()und Sie können sammeln an mehreren Stellen verwenden sharedPref.collect {}. Das einzige, was ist, weil sammeln ist suspendieren, müssen Sie es vom Coroutine-Block aufrufen. Und gerne helfen np :)
Fatih
Bei meiner dritten Frage könnte die Problemumgehung ein Sendekanal sein.
Zoha131
Sie können dieses Commit überprüfen, um Kanäle anstelle von Live-Daten zu verwenden. github.com/android/plaid/pull/770/commits/…
Fatih
1
Ja, du hast recht. Hier kommt der Fluss ins Spiel. Kanäle haben viele Dinge, um die Sie sich kümmern müssen, 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. Also anstelle von Kanal denke ich, dass es besser ist, Flow zu verwenden
Fatih
3

Flow.asLiveData()In den neuen androidx.lifecyclektx-Paketen gibt es eine neue Erweiterungsfunktion . Weitere Informationen finden Sie in meinem Artikel: https://www.netguru.com/codestories/android-coroutines-%EF%B8%8Fin-2020

Samuel Urbanowicz
quelle
Wann müssen wir das nutzen?
IgorGanapolsky
1
Wenn Sie eine API erfüllen möchten, für die LiveData mit einer Flow-Instanz erforderlich ist
Samuel Urbanowicz,
Laut Google müssen wir entweder LiveData oder Flow wählen : codelabs.developers.google.com/codelabs/…
IgorGanapolsky
1

In einer dreistufigen Architektur: Datendomänenpräsentation sollte Flow in der Datenschicht (Datenbanken, Netzwerk, Cache ...) stattfinden. Anschließend können Sie Flow, wie Samuel Urbanowicz erwähnt hat, LiveData zuordnen.

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

gts13
quelle
Für One-Shot-Vorgänge (dh Datenbank lesen) ist LiveData ausreichend.
IgorGanapolsky