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)
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.
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.
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 .
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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With