Migration from MVP to MVVM using Android Architecture Components
A lot has changed since I first developed an Android app. Tech stack is getting better every passing minute and the urge for a more cleaner, maintainable and understandable code is always rising. I built the TvMaze repository with an aim to help Android devs understand the complexities involved while developing an app using MVP design pattern. This repo had used Dagger 2 for dependency injection, Retrofit clubbed with RxJava for asynchronous network calls. Now, the time has changed. Let’s cover on why and how I migrated TvMaze repository from MVP to MVVM.
Why to move from MVP to MVVM?
Let’s uncover the reasons using the sample. The TvMaze app has home screen which displays the Popular Shows to be airing in US for the current day. The previous design using MVP was like this:
HomeView is the interface which is given to the HomePresenter for interaction with the Views. You can switch to the branch master-mvp branch to check all the details. I tried to have the Passive View implementation and moved every business logic to HomePresenterImpl and the repository(HomeEndPoint here). The main problem with MVP is the strict dependency of HomeView with the HomePresenter i.e. my presenters are not as independent as they are supposed to be. They totally govern the state of your View. These hard dependencies also led to many crashes due to IllegalStateException since the presenter does not know when the View is present or it has been destroyed. To fix this, I started adding hacks mainly isViewAttached which checks whether the interface reference is null or not. Some solved it by using RxJava like this:
I had implemented MVP but the hard dependency of presenter with the View was still there, violating the Separation of Concern which was disheartening. Then, came Android Architecture Components and life became so joyous.
The Migration to MVVM
Now, it is time to solve the problem with MVVM using Android Architecture Components. LiveData and ViewModel are the core components which will help in resolving the view dependency. LiveData is an observable data holder class which is Lifecycle Aware i.e. it respects the state of the Activity or Fragment. ViewModel is like the state representation of UI which stores and manages UI-related data in a lifecycle conscious way. It allows data to survive configuration changes such as screen rotations. The MVVM design of TvMaze Home Screen now:
Hola! and the interfaces have gone. Right, HomeView and HomePresenter interfaces are not required now. HomePresenterImpl is replaced with HomeViewModel with slight modifications. The view triggers like showPopularShows which was previously done via HomePresenter through HomeView interface, is now taken care of by MutableLiveData. MutableLiveData is a LiveData which exposes setValue(T)
and postValue(T)
methods. Use MutableLiveData setValue(T)
method if you are on main thread and LiveData postValue(T)
method for the background thread.
The HomeActivity is observing to the LiveData and reacts if any value is set.
The migration from MVP to MVVM was also made smoother by Dagger 2 with Android Injection.
How Dagger helped?
I wanted to move to MVVM with as cleaner approach as possible so that every code can be tested, be it the ViewModels, Activity, Fragment or Repository. With the advent of Dagger 2 Android Injection, things became a lot easier. Now, you don’t need to create separate SubComponent for Activity and it can be easily done using new annotation called ContributesAndroidInjector. Before that, I wanted the ViewModels also to be provided by Dagger. So, I created a factory class TvMazeViewModelFactory which will provide the ViewModels as and when needed. ViewModelModule takes care of providing all the ViewModels used in the app.
ActivityBuildersModule will help in injecting all the activities. If you have fragments in your Activity, you can create a FragmentBuilderModule for each activity and add this as an attribute in annotation ContributesAndroidInjector.
Tip: When you are injecting dependencies, always give preference to Constructor Injection than Field Injection. Using the former allows Dagger to know how the dependency of a class can be fulfilled.
For Ex: This is how the HomeViewModel class looks like with constructor injection.
Dagger has to construct HomeViewModel object, which is dependent on TvMazeApi object which is provided in TvMazeBackendModule.
The dependencies of TvMazeApi is subsequently provided in NetworkModule.
Dagger now knows how to clearly construct the HomeViewModel object. The TvMazeAPI is provided by TvMazeBackEndModule and all its subsequent dependencies are fulfilled in NetworkModule.
Now, it feels good to have a good separation of concern to the View, ViewModel and Repository. Every class knows their responsibility and adheres to it. The code quality has improved and every class can be easily tested. Android Architecture Components is way powerful than you can imagine and it is just the beginning.
You can check the complete diff of MVP to MVVM migration here:
Bonus:
TvMaze repo also has Static Code Analysis integrated using FindBugs, PMD and CheckStyle. Check out the article here:
Want to check how you can implement Dependency Injection in your project.
That’s it for now. Soon, I will be integrating Room and PagingLibrary to the TvMaze repo for a more clearer picture of how things work. Stay tuned!
Update 12th July, 2018:
- Article on the integration of PagingLibrary has been published. Read here:
2. Article on Room Integration has been published. Read here:
Check out all the top articles at blog.mindorks.com