Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Communication between view and ViewModel in MVVM with LiveData

What is a proper way to communicate between the ViewModel and the View, Google architecture components give use LiveData in which the view subscribes to the changes and update itself accordingly, but this communication not suitable for single events, for example show message, show progress, hide progress etc.

There are some hacks like SingleLiveEvent in Googles example but it work only for 1 observer. Some developers using EventBus but i think it can quickly get out of control when the project grows.

Is there a convenience and correct way to implement it, how do you implement it?

(Java examples welcome too)

like image 414
Pavel Poley Avatar asked Nov 26 '19 19:11

Pavel Poley


People also ask

How do you communicate between view and ViewModel?

What is a proper way to communicate between the ViewModel and the View , Google architecture components give use LiveData in which the view subscribes to the changes and update itself accordingly, but this communication not suitable for single events, for example show message, show progress, hide progress etc.

How communication happens in MVVM?

Messages are often packaged into different types, so a ViewModel can subscribe to only listen for specific message types such as CloseApplicationMessages. This kind of loosely coupled event system is ideal for MVVM because the ViewModels don't need to know about each other to be able to do their job.

How can ViewModel communicate with fragments and activity?

To allow a Fragment to communicate up to its Activity , you can define an interface in the Fragment class and implement it within the Activity . The Fragment captures the interface implementation during its onAttach() lifecycle method and can then call the Interface methods in order to communicate with the Activity .

Can a ViewModel be shared between activity and fragment?

TL;DR: We can pass parameters to our ViewModel, use it as a data holder, also to share data between Fragments, and to persist its state across process recreation. This is part of a multi-part series regarding Advanced ViewModels on Android. This post focuses on how our ViewModel can be shared between Fragments.


2 Answers

Yeah I agree, SingleLiveEvent is a hacky solution and EventBus (in my experience) always lead to trouble.

I found a class called ConsumableValue a while back when reading the Google CodeLabs for Kotlin Coroutines, and I found it to be a good, clean solution that has served me well (ConsumableValue.kt):

class ConsumableValue<T>(private val data: T) {
    private var consumed = false

    /**
     * Process this event, will only be called once
     */
    @UiThread
    fun handle(block: ConsumableValue<T>.(T) -> Unit) {
        val wasConsumed = consumed
        consumed = true
        if (!wasConsumed) {
            this.block(data)
        }
    }

    /**
     * Inside a handle lambda, you may call this if you discover that you cannot handle
     * the event right now. It will mark the event as available to be handled by another handler.
     */
    @UiThread
    fun ConsumableValue<T>.markUnhandled() {
        consumed = false
    }
}
class MyViewModel : ViewModel {
    private val _oneShotEvent = MutableLiveData<ConsumableValue<String>>()
    val oneShotEvent: LiveData<ConsumableValue<String>>() = _oneShotData

    fun fireEvent(msg: String) {
        _oneShotEvent.value = ConsumableValue(msg)
    }
}
// In Fragment or Activity
viewModel.oneShotEvent.observe(this, Observer { value ->
    value?.handle { Log("TAG", "Message:$it")}
})

In short, the handle {...} block will only be called once, so there's no need for clearing the value if you return to a screen.

like image 151
patrick.elmquist Avatar answered Oct 26 '22 05:10

patrick.elmquist


What about using Kotlin Flow?

I do not believe they have the same behavior that LiveData has where it would alway give you the latest value. Its just a subscription similar to the workaround SingleLiveEvent for LiveData.

Here is a video explaining the difference that I think you will find interesting and answer your questions

https://youtu.be/B8ppnjGPAGE?t=535

like image 29
tyczj Avatar answered Oct 26 '22 04:10

tyczj