Android Viewmodel Call Activity Methods
Solution 1:
There are several different approaches on how to do this. Here I want to share my approach with you. Which, in my opinion, is the most suitable for MVVM pattern ideology.
As was mentioned - "View Model must know nothing about the View and reference it". This leaves not many options on how a View Model will call an Activity method. First, what comes to mind is a Listener approach. But this approach has several drawbacks in my opinion:
- The
View
should take care of subscribing/unsubscribing to/fromViewModel
, as it's lifetime most likely shorter thanViewModel
's - The first drawback also leads to a situation where something happened and the
ViewModel
should callView
's method but theView
is in between subscribing/unsubscribing;ViewModel
also should aware of empty listener situation as it can benull
- When adding new methods of ViewModel-Activity communication you will have to make changes in
ViewModel
,Activity
andListener
interface.
So the Listener approach doesn't suite quite well. And it looks more like an MVP approach. To eliminate the above-mentioned drawbacks (or at least some of them), I've created, what I call, ViewModel Events approach. In this approach, ViewModel
"emits" (or generates) it's events and lets the View
to observe them. Let me show what I'm talking about.
At first, we will need some representation of the ViewModel
event.
abstractclassViewModelEvent{
var handled: Boolean = falseprivatesetopenfunhandle(activity: BaseActivity) {
handled = true
}
}
As you already can see, the handle()
method will do the magic. When the Activity
will handle received event it will pass its instance to handle()
method as a parameter. Inside this method, we can call any Activity
methods (or safe cast it to some specific Activity
). The handled
property is aimed to not let the Activity
to handle this ViewModelEvent
twice.
Further, we need to create some mechanism for the ViewModel
to emit its events. LiveData
suits the most for these needs. It will cancel an observer subscription on lifecycle events and it will store last emitted event (that is why the ViewModelEvent
should have the above-mentioned handled
property).
abstractclassBaseViewModel: ViewModel() {
privateval observableEvents = MutableLiveData<ViewModelEvent>()
funobserveViewModelEvents(): LiveData<ViewModelEvent> = observableEvents
protectedfunpostViewModelEvent(event: ViewModelEvent) {
observableEvents.postValue(event)
}
}
Nothing complex here. Just a MutableLiveData
(exposed as LiveData
) and a method to emit events. By the way, inside the postViewModelEvent
we can check the thread this method was called from and use MutableLiveData.postValue
or MutableLiveData.setValue
.
And finally, the Activity
itself.
abstractclassBaseActivity: Activity() {
overridefunonCreate(savedInstanceState: Bundle?) {
// ...
viewModel.observeViewModelEvents().observe(this, Observer {
val event = it.takeUnless { it == null || it.handled } ?: return@Observer
handleViewModelAction(event)
})
}
protectedopenfunhandleViewModelAction(event: ViewModelEvent) {
event.handle(this)
}
}
As you can see, general events can be handled in the BaseActivity
, while some specific events can be handled by overriding the handleViewModelAction
method.
This approach can be changed for specific needs. For example, ViewModelEvent
doesn't have to work with Activity
instance and can be used as a "marker" event or it can pass some specific parameters for the required action, etc.
The ViewModel Events approach makes ViewModel-Activity communication robust and seamless. Activity
will have to subscribe once and it will not miss the latest ViewModel
's event.
Solution 2:
the most difficult part of MVVM is View model must not know about view and reference them
This is quite strong restriction.
You have some options about that
1. View model methods receveing context argument
You can make methods receveing context from view(this method is called from view).
After you can instantiate context related variables.
If you are aware about memory leak, just destroy it when view is pause or stop using Lifecycle aware AAC and reinstatiate when resume or start of Activity or Fragment.
About onActivityResult, I think your solution is not bad because API support is like that.
2. get context from view with data binding
in layout xml, you can send view itself with event listener.
<Button
....
android:onClick=“@{(view) -> vm.onClickFacebookLogin(view)}”
Then you can receive view and retrieve context from view in Viewmodel
3. Use AndroidViewModel
AndroidViewModel class is same with ViewModel class without that has Application context.
You can use Application Context with
gerApplication()
Thank you
Solution 3:
well your approach is quite good. But somehow your interface depends on the activity means if you are reusing your view these interface makes no use or may be for that scenario you have to create new interface to solve your problem.
But if you create an instance of Activity then you have control of it.
Post a Comment for "Android Viewmodel Call Activity Methods"