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?


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.


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


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


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
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.