Migration to Model-View-ViewModel Architecture and LiveData in Open Event Organizer App
Open Event Organizer App (Eventyay Organizer App) is the Android app used by event organizers to create and manage events on the Eventyay platform as well as check-in and check-out attendees along with other functionalities. The app used the MVP (Model-View-Presenter) architecture and is being ported to MVVM (Model-View-ViewModel). This article will explain the procedure of migrating MVP to MVVM architecture and implementing LiveData.
Why migrate to MVVM?
The MVVM architecture is designed to store and manage UI-related data in a lifecycle conscious way. Configuration changes such as screen rotations are handled properly by ViewModels.
Tight Coupling:
The issue of tight coupling is resolved since only the View holds the reference to ViewModel and not vice versa. A single View can hold references to multiple ViewModels.
Testability:
Since Presenters are hard bound to Views, writing unit tests becomes slightly difficult as there is a dependency of a View.
ViewModels are more unit test friendly as they can be independently tested. There is no dependency of the View.
Here, the implementation is being described with the example of About Event module in the Open Event Organizer App.
First step is the creation of a new class AboutEventViewModel which extends ViewModel.
@Binds @IntoMap @ViewModelKey(AboutEventViewModel.class) public abstract ViewModel bindAboutEventViewModel(AboutEventViewModel aboutEventViewModel);
The new ViewModel has to be added to the ViewModelModule:
Constructor for the ViewModel:
@Inject public AboutEventViewModel(EventRepository eventRepository, CopyrightRepository copyrightRepository, DatabaseChangeListener<Copyright> copyrightChangeListener) { this.eventRepository = eventRepository; this.copyrightRepository = copyrightRepository; this.copyrightChangeListener = copyrightChangeListener; eventId = ContextManager.getSelectedEvent().getId(); }
We are using Dagger2 for dependency injection.
LiveData
LiveData is a lifecycle-aware data holder with the observer pattern.
When we have a LiveData object (e.g. list of attendees), we can add some LifecycleOwner (it can be Activity or Fragment) as an observer. Using this:
The Activity or Fragment will remain updated with the data changes.
Observers are only notified if they are in the STARTED or RESUMED state which is also known as the active state. This prevents memory leaks and NullPointerExceptions because inactive observers are not notified about changes.
Now, let’s discuss about the implementation of LiveData. We will create objects of SingleEventLiveData<> class.
private final SingleEventLiveData<Boolean> progress = new SingleEventLiveData<>(); private final SingleEventLiveData<String> error = new SingleEventLiveData<>(); private final SingleEventLiveData<Event> success = new SingleEventLiveData<>(); private final SingleEventLiveData<Copyright> showCopyright = new SingleEventLiveData<>(); private final SingleEventLiveData<Boolean> changeCopyrightMenuItem = new SingleEventLiveData<>(); private final SingleEventLiveData<String> showCopyrightDeleted = new SingleEventLiveData<>();
The functions to get the LiveData objects:
public LiveData<Boolean> getProgress() { return progress; } public LiveData<Event> getSuccess() { return success; } public LiveData<String> getError() { return error; } public LiveData<Copyright> getShowCopyright() { return showCopyright; } public LiveData<Boolean> getChangeCopyrightMenuItem() { return changeCopyrightMenuItem; } public LiveData<String> getShowCopyrightDeleted() { return showCopyrightDeleted; }
Now, we can remove getView() methods and instead, these objects will be used to call various methods defined in the fragment.
Let’s discuss the changes required in the AboutEventFragment now.
The Fragment will have ViewModelProvider.Factory injected.
@Inject ViewModelProvider.Factory viewModelFactory;
Declare an object of the ViewModel.
private AboutEventViewModel aboutEventViewModel;
Then, in onCreateView(), viewModelFactory will be passed to the ViewModelProviders.of() method as the factory, which is the second parameter.
aboutEventViewModel = ViewModelProviders.of(this, viewModelFactory).get(AboutEventViewModel.class);
Replace all references to the Presenter with references to the ViewModel.
Add the Fragment as an observer to the changes by adding the following in the onStart() method:
aboutEventViewModel.getProgress().observe(this, this::showProgress); aboutEventViewModel.getSuccess().observe(this, this::showResult); aboutEventViewModel.getError().observe(this, this::showError); aboutEventViewModel.getShowCopyright().observe(this, this::showCopyright); aboutEventViewModel.getChangeCopyrightMenuItem().observe(this, this::changeCopyrightMenuItem); aboutEventViewModel.getShowCopyrightDeleted().observe(this, this::showCopyrightDeleted);
Two parameters are passed to the observe() method – first one is LifecycleOwner, which is our Fragment in this case. The second one is a callback along with a parameter and is used to call the required method.
With this, the implementation of MVVM and LiveData is brought to completion.
Resources:
Documentation: ViewModel, LiveData
Further reading:
Open Event Organizer App: Project repo, Play Store, F-Droid