Introduction
Why MVVM, when there are various other architectures available, such as MVC or MVP? MVP has been a very popular architecture among Android developers but it has some disadvantages. First, we must know its architecture. Architecture is "Building an application with a certain set of rules along with the proper implementation of protocols and functionalities." Good architecture helps with the maintainability and testability of code. Consider a situation where you don't have any architecture and there are lots of activities, fragments, adapters, model classes and so on in a single unit. If you want to change something in a fragment, then searching and changing a line of code is like searching through a favourite t-shirt in a large pile of clothes. It seems messy and takes a lot of time to do that. So, why MVVM?
Why MVVM?
Why not MVP or something else. Well, let's talk about a very popular architecture, Model View Presenter (MVP).
- Model: Model has the manipulation of data and is business logic. The model does not directly contact with a view, instead, its communicated by the presenter.
- View: View is mainly the UI part. It also communicates with the presenter. View has the presenter as a reference.
- Presenter: It presents the data to the view which is obtained from the model. The Presenter has the reference of the View. Actually, View and Presenter here are tightly coupled.

Here we can see that Presenter is responsible for updating View with the data. There is a disadvantage to this (MVP) approach:
- There is a tight coupling between Presenter and View. If an Activity is destroyed and the presenter keeps trying to update the view, the application crashes because view was not there for some reason. It's the same case with the configuration change.
- Interfaces are very huge in number, acting between these layers.
MVVM
MVVM stands for Model View ViewModel. It has overcome this issue in the MVP approach by making the components loosely coupled. It works on the concept of observables. Children don't have reference to the parent, they only have reference by observables.
Model
It has business logic with a repository, which in turn has local and remote data classes. The repository communicates with local or remote data sources according to the request from ViewModel.
View
View consists of a UI part (i.e., Activity, Fragment and so on). Clickable buttons or any action is sent to the ViewModel, but does not directly get a response. To get a response, view observes some data which ViewModel exposes. This means that it works on the Observer and Observable pattern.
ViewModel
ViewModel has all UI logic part there. ViewModel works as a bridge between View and Model class. ViewModel does not have a direct reference to the View class, instead, it sends data through observables. The View part has observed data and UI gets updated when data changes observable notifies.
Let's see the image for better understanding:

How does this differ from MVP?
- ViewModel replaces the Presenter in the Middle Layer.
- The Presenter holds references to the View. The ViewModel doesn’t.
- The Presenter updates the View using the classical way (triggering methods).
- The ViewModel sends data streams.
- The ViewModel does not have a clue about the View or Views is listening to it.
Here is how we are going to implement this info:
MVVM using LiveData
This is an Observable data holder class. Let's see some brief information about LiveData.
LiveData
According to the docs "LiveData is an observable data holder class. Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. This awareness ensures LiveData only updates app component observers that are in an active lifecycle state.".
Advantages of LiveData
- No memory leaks: Observers are bound to Lifecycle objects and clean up after themselves when their associated lifecycle is destroyed.
-
No crashes due to stopped activities: If the view is destroyed or is in backstack, it will not update the view. Crashing can be avoided using this approach.
- No more manual lifecycle handling: Since LiveData automatically manages the lifecycle changes in UI since it is lifecycle aware.
-
Proper configuration change: If configuration change happens when the device gets rotated, then it receives the latest available data.
- apply plugin: 'com.android.application'
- android {
- compileSdkVersion 28
- defaultConfig {
- applicationId "com.example.com"
- minSdkVersion 21
- targetSdkVersion 28
- versionCode 1
- versionName "1.0"
- testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
- }
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- }
- }
- }
- dependencies {
- implementation fileTree(dir: 'libs', include: ['*.jar'])
- implementation 'com.android.support:appcompat-v7:28.0.0'
- implementation 'com.android.support.constraint:constraint-layout:1.1.3'
- testImplementation 'junit:junit:4.12'
- androidTestImplementation 'com.android.support.test:runner:1.0.2'
- androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
- // Lifecycle components
- def archLifecycleVersion = '1.1.1'
- implementation "android.arch.lifecycle:extensions:$archLifecycleVersion"
- annotationProcessor "android.arch.lifecycle:compiler:$archLifecycleVersion"
- // Glide
- def glideVersion = '4.8.0'
- implementation "com.github.bumptech.glide:glide:$glideVersion"
- annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion"
- //RecyclerView
- implementation 'com.android.support:recyclerview-v7:28.0.0'
- //Support Design for Coordinator layout
- implementation 'com.android.support:design:28.0.0'
- //circle imageview
- implementation 'de.hdodenhof:circleimageview:2.2.0'
- }
- <uses-permission android:name="android.permission.INTERNET"/>
- public class NicePlace {
- private String title;
- private String imageUrl;
- public NicePlace(String imageUrl, String title) {
- this.title = title;
- this.imageUrl = imageUrl;
- }
- public NicePlace() {
- }
- public String getTitle() {
- return title;
- }
- public void setTitle(String title) {
- this.title = title;
- }
- public String getImageUrl() {
- return imageUrl;
- }
- public void setImageUrl(String imageUrl) {
- this.imageUrl = imageUrl;
- }
- }
- /**
- * Singleton pattern
- */
- public class NicePlaceRepository {
- private static NicePlaceRepository instance;
- private ArrayList<NicePlace> dataSet = new ArrayList<>();
- public static NicePlaceRepository getInstance(){
- if(instance == null){
- instance = new NicePlaceRepository();
- }
- return instance;
- }
- // Pretend to get data from a webservice or online source
- public MutableLiveData<List<NicePlace>> getNicePlaces(){
- setNicePlaces();
- MutableLiveData<List<NicePlace>> data = new MutableLiveData<>();
- data.setValue(dataSet);
- return data;
- }
- private void setNicePlaces(){
- dataSet.add(
- new NicePlace("https://c1.staticflickr.com/5/4636/25316407448_de5fbf183d_o.jpg",
- "Falls")
- );
- dataSet.add(
- new NicePlace("https://i.redd.it/tpsnoz5bzo501.jpg",
- "Trondheim")
- );
- dataSet.add(
- new NicePlace("https://i.redd.it/qn7f9oqu7o501.jpg",
- "Spain")
- );
- dataSet.add(
- new NicePlace("https://i.redd.it/j6myfqglup501.jpg",
- "Rocky Mountain Park")
- );
- dataSet.add(
- new NicePlace("https://i.redd.it/0h2gm1ix6p501.jpg",
- "Havasu Falls")
- );
- dataSet.add(
- new NicePlace("https://i.redd.it/k98uzl68eh501.jpg",
- "Mahahual")
- );
- dataSet.add(
- new NicePlace("https://c1.staticflickr.com/5/4636/25316407448_de5fbf183d_o.jpg",
- "Lake")
- );
- dataSet.add(
- new NicePlace("https://i.redd.it/obx4zydshg601.jpg",
- "Austrailia")
- );
- }
- }




Pravesh DubeyPosted Mar 16, 2020, 5:00 AM
A well-explained article.