Osservazione di LiveData da ViewModel


95

Ho una classe separata in cui gestisco il recupero dei dati (in particolare Firebase) e di solito restituisco gli oggetti LiveData da essa e li aggiorno in modo asincrono. Ora voglio che i dati restituiti siano archiviati in un ViewModel, ma il problema è che per ottenere tale valore, devo osservare l'oggetto LiveData restituito dalla mia classe di recupero dati. Il metodo di osservazione richiedeva un oggetto LifecycleOwner come primo parametro, ma ovviamente non lo ho all'interno del mio ViewModel e so che non dovrei mantenere un riferimento all'attività / frammento all'interno del ViewModel. Cosa dovrei fare?


Risposte:


39

In questo post del blog dello sviluppatore di Google Jose Alcérreca si consiglia di utilizzare una trasformazione in questo caso (vedere il paragrafo "LiveData nei repository") perché ViewModel non dovrebbe contenere alcun riferimento relativo a View(Attività, Contesto ecc.) Perché lo rende difficile testare.


sei riuscito a far funzionare la trasformazione per te? I miei eventi non funzionano
romaneso

26
Le trasformazioni da sole non funzionano, poiché qualunque codice si scrive nella trasformazione viene allegato per essere eseguito solo quando un'entità osserva la trasformazione .
orbitbot

9
Non so perché questa sia la risposta consigliata, non ha nulla a che fare con la domanda. 2 anni dopo, e ancora non sappiamo come osservare le modifiche ai dati del repository nel nostro viewmodel.
Andrew

26

Nella documentazione di ViewModel

Tuttavia, gli oggetti ViewModel non devono mai osservare le modifiche agli osservabili sensibili al ciclo di vita, come gli oggetti LiveData.

Un altro modo è che i dati implementino RxJava piuttosto che LiveData, quindi non avranno il vantaggio di essere consapevoli del ciclo di vita.

Nell'esempio Google di todo-mvvm-live-kotlin , utilizza una richiamata senza LiveData in ViewModel.

Immagino che se vuoi rispettare l'intera idea di essere un prodotto del ciclo di vita, dobbiamo spostare il codice di osservazione in Activity / Fragment. Altrimenti, possiamo usare callback o RxJava in ViewModel.

Un altro compromesso è implementare MediatorLiveData (o Transformations) e osservare (inserire qui la logica) in ViewModel. Si noti che l'osservatore MediatorLiveData non si attiverà (come le trasformazioni) a meno che non venga osservato in Activity / Fragment. Quello che facciamo è inserire un'osservazione vuota in Activity / Fragment, dove il lavoro reale viene effettivamente svolto in ViewModel.

// 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: ho letto ViewModels e LiveData: Patterns + AntiPatterns che suggerivano Transformations. Non penso che funzioni a meno che non venga osservato LiveData (che probabilmente richiede che venga eseguito in Activity / Fragment).


2
È cambiato qualcosa al riguardo? O RX, richiamata o osservazione in bianco sono solo soluzioni?
qbait

2
Qualche soluzione per sbarazzarsi di queste osservazioni in bianco?
Ehsan Mashhadi

1
Forse usando Flow ( mLiveData.asFlow()) o observeForever.
Machado

La soluzione di flusso sembra funzionare se non vuoi avere / non hai bisogno di alcuna logica di osservazione in Fragment
adek111

14

Penso che tu possa usare osservareForever che non richiede l'interfaccia del proprietario del ciclo di vita e puoi osservare i risultati dal viewmodel


2
questa mi sembra la risposta giusta soprattutto che nei documenti su ViewModel.onCleared () si dice: "È utile quando ViewModel osserva alcuni dati ed è necessario cancellare questa sottoscrizione per evitare una perdita di questo ViewModel."
Yosef

2
Scusa maCannot invoke observeForever on a background thread
Boken

1
Sembra abbastanza legittimo. Sebbene sia necessario salvare gli osservatori nei campi viewModel e annullare l'iscrizione a onCleared. Per quanto riguarda il thread in background, osserva dal thread principale, il gioco è fatto.
Kirill Starostin

@Boken Puoi forzare la chiamata observeForeverdalla via principaleGlobalScope.launch(Dispatchers.Main) { myvm.observeForever() }
rmirabelle

5

Usa le coroutine di Kotlin con i componenti Architecture.

È possibile utilizzare la liveDatafunzione builder per chiamare una suspendfunzione, servendo il risultato come un LiveDataoggetto.

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

È inoltre possibile emettere più valori dal blocco. Ogni emit()chiamata sospende l'esecuzione del blocco fino a quando il LiveDatavalore non viene impostato sul thread principale.

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

Nella tua configurazione gradle, usa androidx.lifecycle:lifecycle-livedata-ktx:2.2.0o superiore.

C'è anche un articolo a riguardo.

Aggiornamento : inoltre è possibile modificare LiveData<YourData>in Dao interface. Devi aggiungere la suspendparola chiave alla funzione:

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

e in ViewModelè necessario ottenerlo in modo asincrono in questo modo:

viewModelScope.launch(Dispatchers.IO) {
    allData = dao.getAll()
    // It's also possible to sync other data here
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.