Compte tenu de la configuration suivante:
J'ai 2 référentiels: Repository A et Repository B tous les deux renvoient des données actives.
J'ai un ViewModel qui utilise ces deux référentiels.
Je souhaite extraire quelque chose du référentiel A et en fonction du résultat, je souhaite extraire quelque chose du référentiel B, puis transformer le résultat avant de revenir à l'interface utilisateur.
Pour cela, je me suis intéressé aux classes LiveData Transformation . Les exemples montrent une seule transformation du résultat, mais je souhaite quelque chose dans le sens de l'enchaînement de deux transformations. Comment puis-je accomplir cela?
J'ai essayé de mettre en place quelque chose comme ça, mais il y a une incompatibilité de type sur le deuxième bloc de transformation:
internal val launchStatus: LiveData<String> = Transformations
.map(respositoryA.getData(), { data ->
if (data.isValid){
"stringA"
} else {
//This gives a type mismatch for the entire block
Transformations.map(repositoryB.getData(), {
result -> result.toString()
})
}
})
(Veuillez également me faire savoir s'il existe une approche alternative/recommandée pour saisir quelque chose pour les chaîner, par exemple, saisir quelque chose de A puis prendre quelque chose de B en fonction du résultat de A, etc.)
J'ai utilisé MediatorLiveData pour résoudre ce problème.
MediatorLiveData peut observer d'autres objets LiveData
et y réagir.
Au lieu d’observer l’un des dépôts. J'ai créé myData (instance de MediatorLiveData) dans ma classe ViewModel
et j'ai le droit d'observer cet objet. J'ajoute ensuite le référentiel A en tant que source initiale et observe cela et ajoute uniquement Repository B si le résultat de A le requiert. Cela me permet de conserver les transformations associées aux données en temps réel de chaque rapport et de traiter chaque résultat dans le bon ordre. Voir ci-dessous pour la mise en œuvre:
internal val myData: MediatorLiveData<String> = MediatorLiveData()
private val repoA: LiveData<String> = Transformations.map(
respositoryA.getData(), { data ->
if (data.isValid) "stringA" else ""
})
private val repoB: LiveData<String> = Transformations.map(
repositoryB.getData(), { data -> "stringB"
})
fun start() {
myData.addSource(repoA, {
if (it == "stringA") {
myData.value = it
} else {
myData.addSource(repoB, {
myData.value = it
})
}
})
}
Note: La solution ne couvre pas le cas où le repoB pourrait être ajouté plusieurs fois, mais il devrait être assez simple à gérer.
Votre lambda renvoie parfois la String
"stringA"
et parfois le LiveData<String>
donné par:
Transformations.map(repositoryB.getData(), {
result -> result.toString()
})
Cela signifie que votre lambda n'a pas de sens - il renvoie différentes choses dans différentes branches.
Comme d'autres l'ont déjà mentionné, vous pourriez écrire votre propre MediatorLiveData
au lieu d'utiliser celle donnée par Transformations
. Cependant, je pense qu'il est plus facile de faire ce qui suit:
internal val launchStatus: LiveData<String> = Transformations
.switchMap(respositoryA.getData(), { data ->
if (data.isValid) {
MutableLiveData().apply { setValue("stringA") }
} else {
Transformations.map(repositoryB.getData(), {
result -> result.toString()
})
}
})
Tout ce que j'ai fait est de faire en sorte que la première branche de code retourne également un LiveData<String>
; votre lambda est donc logique - c'est un (String) -> LiveData<String>
Je devais faire un autre changement: utiliser switchMap
au lieu de map
. En effet, map
prend un lambda (X) -> Y
, mais switchMap
prend un lambda (X) -> LiveData<Y>
.
J'essaierais d'utiliser switchMap au lieu de map:
Comme pour map (), applique une fonction à la valeur stockée dans l'objet LiveData, décompresse et distribue le résultat en aval. La fonction transmise à switchMap () doit renvoyer un objet LiveData.
Vous pouvez transformer les données en utilisant switchmap
. Voici un exemple documentation .