Observando LiveData de ViewModel

95

Tenho uma classe separada em que lido com a busca de dados (especificamente Firebase) e normalmente retorno objetos LiveData a partir dela e os atualizo de forma assíncrona. Agora quero ter os dados retornados armazenados em um ViewModel, mas o problema é que, para obter esse valor, preciso observar o objeto LiveData retornado da minha classe de busca de dados. O método observar exigia um objeto LifecycleOwner como o primeiro parâmetro, mas obviamente não tenho isso dentro do meu ViewModel e sei que não devo manter uma referência à Activity / Fragment dentro do ViewModel. O que devo fazer?

Vuk Bibic
fonte

Respostas:

39

Em este blog pelo Google desenvolvedor Jose alcerreca é recomendado o uso de uma transformação neste caso (ver o "LiveData em repositórios" parágrafo) porque ViewModel não deverá manter qualquer referência relacionada com View(Activity, Contexto etc), porque ele tornava difícil testar.

guglhupf
fonte
você conseguiu fazer o Transformation trabalhar para você? Meus eventos não estão funcionando
romaneso
26
As transformações por si só não funcionam, pois qualquer código que você escreve na transformação só é anexado para execução quando alguma entidade observa a transformação .
orbitbot
9
Não sei porque é a resposta recomendada, não tem nada a ver com a pergunta. 2 anos depois, e ainda não sabemos como observar as mudanças nos dados do repositório em nosso modelo de visão.
Andrew,
26

Na documentação do ViewModel

No entanto, os objetos ViewModel nunca devem observar mudanças nos observáveis ​​que reconhecem o ciclo de vida, como objetos LiveData.

Outra maneira é os dados implementarem RxJava em vez de LiveData, então não terão o benefício de serem cientes do ciclo de vida.

Na amostra do google de todo-mvvm-live-kotlin , ele usa um retorno de chamada sem LiveData em ViewModel.

Eu estou supondo que se você deseja cumprir com a ideia de ser um software de ciclo de vida, precisamos mover o código de observação em Activity / Fragment. Caso contrário, podemos usar callback ou RxJava em ViewModel.

Outro compromisso é implementar MediatorLiveData (ou Transformations) e observar (colocar sua lógica aqui) em ViewModel. Observe que o observador MediatorLiveData não disparará (o mesmo que Transformations), a menos que seja observado em Activity / Fragment. O que fazemos é colocar um vazio observe em Activity / Fragment, onde o trabalho real é realmente feito em 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: Eu li ViewModels e LiveData: Patterns + AntiPatterns que sugeriam Transformations. Não acho que funcione a menos que o LiveData seja observado (o que provavelmente requer que seja feito em Activity / Fragment).

Desmond Lua
fonte
2
Algo mudou a esse respeito? Ou RX, retorno de chamada ou observação em branco são apenas soluções?
qbait de
2
Alguma solução para se livrar dessas observações em branco?
Ehsan Mashhadi
1
Talvez usando Flow ( mLiveData.asFlow()) ou observeForever.
Machado
A solução de fluxo parece funcionar se você não quer / não precisa de nenhuma lógica de observador no Fragment
adek111
14

Eu acho que você pode usar o observeForever, que não requer a interface do proprietário do ciclo de vida, e você pode observar os resultados do modelo de visualização

Sidharth
fonte
2
esta parece ser a resposta certa para mim, especialmente porque nos documentos sobre ViewModel.onCleared () é dito: "É útil quando ViewModel observa alguns dados e você precisa limpar esta assinatura para evitar um vazamento deste ViewModel."
Yosef de
2
Desculpe, masCannot invoke observeForever on a background thread
Boken
1
Isso parece bastante legítimo. Embora seja necessário salvar observadores nos campos viewModel e cancelar a assinatura em onCleared. Quanto ao tópico de fundo - observe do tópico principal, é isso.
Kirill Starostin
@Boken Você pode forçar observeForevera invocação do main viaGlobalScope.launch(Dispatchers.Main) { myvm.observeForever() }
rmirabelle
5

Use corrotinas Kotlin com componentes de arquitetura.

Você pode usar a liveDatafunção de construtor para chamar uma suspendfunção, servindo o resultado como um LiveDataobjeto.

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

Você também pode emitir vários valores do bloco. Cada emit()chamada suspende a execução do bloco até que o LiveDatavalor seja definido na thread principal.

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

Na configuração do Gradle, use androidx.lifecycle:lifecycle-livedata-ktx:2.2.0ou superior.

Também existe um artigo sobre isso.

Atualização : Também é possível alterar LiveData<YourData>no Dao interface. Você precisa adicionar a suspendpalavra-chave à função:

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

e no ViewModelvocê precisa obtê-lo de forma assíncrona assim:

viewModelScope.launch(Dispatchers.IO) {
    allData = dao.getAll()
    // It's also possible to sync other data here
}
Psicopata
fonte