LiveData von ViewModel aus beobachten


92

Ich habe eine separate Klasse, in der ich Daten abrufe (insbesondere Firebase), und ich gebe normalerweise LiveData-Objekte von dieser zurück und aktualisiere sie asynchron. Jetzt möchte ich die zurückgegebenen Daten in einem ViewModel speichern, aber das Problem ist, dass ich das LiveData-Objekt beobachten muss, das von meiner Datenabrufklasse zurückgegeben wird, um diesen Wert zu erhalten. Die Observ-Methode erforderte ein LifecycleOwner-Objekt als ersten Parameter, aber das habe ich offensichtlich nicht in meinem ViewModel und ich weiß, dass ich keinen Verweis auf die Aktivität / das Fragment im ViewModel behalten soll. Was soll ich machen?


Antworten:


38

In diesem Blog-Beitrag des Google-Entwicklers Jose Alcérreca wird empfohlen, in diesem Fall eine Transformation zu verwenden (siehe Abschnitt "LiveData in Repositorys"), da ViewModel keinen Verweis auf View(Aktivität, Kontext usw.) enthalten sollte, da dies die Arbeit erschwert zu testen.


Haben Sie es geschafft, Transformation für Sie zum Laufen zu bringen? Meine Veranstaltungen funktionieren nicht
Romaneso

24
Transformationen alleine funktionieren nicht, da der Code, den Sie in die Transformation schreiben, nur zur Ausführung angehängt wird, wenn eine Entität die Transformation beobachtet .
Orbitbot

8
Ich weiß nicht, warum dies die empfohlene Antwort ist, sie hat nichts mit der Frage zu tun. 2 Jahre später wissen wir immer noch nicht, wie wir Änderungen der Repository-Daten in unserem Ansichtsmodell beobachten können.
Andrew

25

In der ViewModel- Dokumentation

ViewModel-Objekte dürfen jedoch niemals Änderungen an lebenszyklusbewussten Observablen wie LiveData-Objekten beobachten.

Eine andere Möglichkeit besteht darin, dass die Daten RxJava anstelle von LiveData implementieren. Dann haben sie nicht den Vorteil, dass sie den Lebenszyklus berücksichtigen.

In einem Google-Beispiel von todo-mvvm-live-kotlin wird in ViewModel ein Rückruf ohne LiveData verwendet.

Ich vermute, wenn Sie der gesamten Idee, Lebenszyklusware zu sein, entsprechen möchten, müssen wir den Beobachtungscode in Aktivität / Fragment verschieben. Andernfalls können wir in ViewModel Callback oder RxJava verwenden.

Ein weiterer Kompromiss besteht darin, MediatorLiveData (oder Transformationen) zu implementieren und in ViewModel zu beobachten (hier Ihre Logik einfügen). Beachten Sie, dass der MediatorLiveData-Beobachter nur dann ausgelöst wird (wie Transformationen), wenn er in Aktivität / Fragment beobachtet wird. Wir setzen eine leere Beobachtung in Aktivität / Fragment, wo die eigentliche Arbeit tatsächlich in ViewModel erledigt wird.

// ViewModel
fun start(id : Long) : LiveData<User>? {
    val liveData = MediatorLiveData<User>()
    liveData.addSource(dataSource.getById(id), Observer {
        if (it != null) {
            // put your logic here
        }
    })
}

// Activity/Fragment
viewModel.start(id)?.observe(this, Observer {
    // blank observe here
})

PS: Ich habe ViewModels und LiveData: Patterns + AntiPatterns gelesen , die Transformationen vorgeschlagen haben. Ich denke nicht, dass es funktioniert, wenn die LiveData nicht beobachtet werden (was wahrscheinlich erfordert, dass es bei Aktivität / Fragment durchgeführt wird).


2
Hat sich diesbezüglich etwas geändert? Oder RX, Callback oder Blank Observ sind nur Lösungen?
Qbait

2
Gibt es eine Lösung, um diese leeren Beobachtungen loszuwerden?
Ehsan Mashhadi

1
Vielleicht mit Flow ( mLiveData.asFlow()) oder observeForever.
Machado

Die Flow-Lösung scheint zu funktionieren, wenn Sie in Fragment
adek111

14

Ich denke, Sie können watchForever verwenden, für das die Benutzeroberfläche des Lebenszyklusbesitzers nicht erforderlich ist, und Sie können die Ergebnisse des Ansichtsmodells beobachten


2
Dies scheint mir die richtige Antwort zu sein, insbesondere, dass in den Dokumenten zu ViewModel.onCleared () Folgendes steht: "Es ist nützlich, wenn ViewModel einige Daten beobachtet und Sie dieses Abonnement löschen müssen, um ein Durchsickern dieses ViewModel zu verhindern."
Josef

2
Sorry aberCannot invoke observeForever on a background thread
Boken

1
Das scheint ziemlich legitim zu sein. Man muss jedoch Beobachter in den viewModel-Feldern speichern und sich abmelden onCleared. Was den Hintergrund-Thread betrifft - vom Haupt-Thread aus beobachten, das war's.
Kirill Starostin

@Boken Sie können erzwingen observeForever, dass Sie von main viaGlobalScope.launch(Dispatchers.Main) { myvm.observeForever() }
rmirabelle

5

Verwenden Sie Kotlin-Coroutinen mit Architekturkomponenten.

Mit der liveDataBuilder-Funktion können Sie eine suspendFunktion aufrufen und das Ergebnis als LiveDataObjekt bereitstellen.

val user: LiveData<User> = liveData {
    val data = database.loadUser() // loadUser is a suspend function.
    emit(data)
}

Sie können auch mehrere Werte aus dem Block ausgeben. Jeder emit()Aufruf unterbricht die Ausführung des Blocks, bis der LiveDataWert im Hauptthread festgelegt ist.

val user: LiveData<Result> = liveData {
    emit(Result.loading())
    try {
        emit(Result.success(fetchUser()))
    } catch(ioException: Exception) {
        emit(Result.error(ioException))
    }
}

Verwenden Sie in Ihrer Gradle-Konfiguration androidx.lifecycle:lifecycle-livedata-ktx:2.2.0oder höher.

Es gibt auch einen Artikel darüber.

Update : Es ist auch möglich, LiveData<YourData>in der zu ändern Dao interface. Sie müssen suspendder Funktion das Schlüsselwort hinzufügen :

@Query("SELECT * FROM the_table")
suspend fun getAll(): List<YourData>

und in der ViewModelmüssen Sie es so asynchron bekommen:

viewModelScope.launch(Dispatchers.IO) {
    allData = dao.getAll()
    // It's also possible to sync other data here
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.