Kotlin Flow vs Android LiveData

20

J'ai des questions sur Kotlin Flow

  1. Je peux observer LiveData à partir de plusieurs fragments. Puis-je le faire avec Flow? Si oui, alors comment?
  2. Nous pouvons avoir plusieurs LiveData à partir d'un seul LiveData en utilisant map& switchMap. Existe-t-il un moyen d'avoir plusieurs flux à partir d'un seul flux source?
  3. En utilisant MutableLiveDataJe peux mettre à jour les données de n'importe où en utilisant la référence de variable. Existe-t-il un moyen de faire de même avec Flow?

J'ai un cas d'utilisation comme: j'observerai une SharedPreferencesutilisation callbackFlow{...}qui me donnera un flux source unique. À partir de ce flux, je souhaite créer plusieurs flux pour chaque paire clé-valeur.

Ces questions peuvent sembler stupides. Je suis nouveau dans le monde Rx et Flow.

zoha131
la source
Quelle approche avez-vous choisie - Flow ou LiveData ?
IgorGanapolsky
2
Actuellement, j'utilise LiveData pour les vues et Flow pour tout le reste. Dans ViewModel, je reçois Flow et émets LiveData à observer à partir de fragments.
zoha131
@ zoha131 vous le faites de la bonne façon! Étant donné que LiveData ne peut être observé que sur le thread principal, ils s'adaptent parfaitement aux interactions View <-> ViewModel. Ensuite, Flows vous permet d'effectuer des opérations plus complexes dans le reste de votre architecture.
smora

Réponses:

15

Je peux observer LiveData à partir de plusieurs fragments. Puis-je le faire avec Flow? Si oui, alors comment?

Oui. Vous pouvez le faire avec emitet collect. Think emitest similaire aux données en direct postValueet collectest similaire à observe. Donnons un exemple.

Dépôt

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

Nous pouvons avoir plusieurs LiveData à partir d'un seul LiveData en utilisant map & switchMap. Existe-t-il un moyen d'avoir plusieurs flux à partir d'un seul flux source?

Le flux est très pratique. Vous pouvez simplement créer un flux à l'intérieur du flux. Supposons que vous souhaitiez ajouter un signe de degré à chacune des données de prévisions météorologiques.

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

Ensuite, collectez les données dans le fragment identique à # 1. Ici, ce qui se passe, c'est que le modèle de vue collecte les données du référentiel et que le fragment collecte les données du modèle de vue.

En utilisant MutableLiveData, je peux mettre à jour les données de n'importe où en utilisant la référence de variable. Existe-t-il un moyen de faire de même avec Flow?

Vous ne pouvez pas émettre de valeur en dehors du flux. Le bloc de code à l'intérieur du flux n'est exécuté que lorsqu'il existe un collecteur. Mais vous pouvez convertir le flux en données en direct en utilisant l'extension asLiveData de LiveData.

ViewModel

fun getWeatherForecast(): LiveData<String> {
    return forecastRepository
    .getWeatherForecastEveryTwoSeconds()
    .asLiveData() // Convert flow to live data
}

Dans votre cas, vous pouvez le faire

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
}

Éditer

Merci à @mark pour son commentaire. La création d'un nouveau flux dans le modèle de vue pour la getWeatherForecastfonction n'est en fait pas nécessaire. Il pourrait être réécrit comme

fun getWeatherForecast(): Flow<String> {
        return forecastRepository
                .getWeatherForecastEveryTwoSeconds(spendingDetailsRequest)
                    .map {
                        it + " °C"
                    }
    }
Fatih
la source
Je ne sais pas pourquoi, mais je supposais que je ne pouvais pas appeler la fonction collect () à plusieurs endroits pour un seul flux. Merci d'avoir répondu.
zoha131
1
Non. Vous pouvez collecter le même flux à plusieurs endroits. val sharedPref = getSharedPref()et vous pouvez utiliser la collecte à plusieurs endroits sharedPref.collect {}. La seule chose est que la collecte est suspendue, vous devez l'appeler à partir du bloc coroutine. Et heureux d'aider np :)
Fatih
pour ma troisième question, la solution de contournement pourrait être une chaîne de diffusion.
zoha131
Vous pouvez vérifier ce commit pour utiliser des canaux au lieu de données en direct. github.com/android/plaid/pull/770/commits/…
Fatih
1
Oui, tu as raison. C'est là que le flux entre en jeu. Les canaux ont beaucoup de choses dont vous devez vous occuper et ils sont chauds, ce qui signifie qu'ils sont toujours ouverts même s'il n'y a pas d'observateurs. Mais avec le débit, vous pouvez obtenir les mêmes avantages sans aucun souci car ils sont froids. Donc, au lieu du canal, je pense qu'il vaut mieux utiliser le flux
Fatih
3

Il y a une nouvelle Flow.asLiveData()fonction d'extension dans les nouveaux androidx.lifecyclepackages ktx. Vous pouvez en savoir plus dans mon article: https://www.netguru.com/codestories/android-coroutines-%EF%B8%8Fin-2020

Samuel Urbanowicz
la source
Quand devons-nous l'utiliser?
IgorGanapolsky
1
Lorsque vous souhaitez satisfaire une API qui nécessite LiveData avec une instance Flow
Samuel Urbanowicz
Selon google, nous devons choisir entre LiveData ou Flow: codelabs.developers.google.com/codelabs/…
IgorGanapolsky
1

Dans une architecture à 3 niveaux: présentation du domaine des données, Flow devrait avoir lieu dans la couche de données (bases de données, réseau, cache ...) puis, comme Samuel Urbanowicz l'a mentionné, vous pouvez mapper Flow sur LiveData.

En général, Flow est presque ce que l'Observable (ou Flowable) est pour RxJava. Ne le confondez pas avec LiveData.

plus ici: https://medium.com/@elizarov/cold-flows-hot-channels-d74769805f9

gts13
la source
Pour les opérations ponctuelles (c'est-à-dire la lecture de la base de données), LiveData est suffisant.
IgorGanapolsky