MVVM architecture pattern

In this blog we are going to see about the importance of architecture pattern and MVVM architecture pattern in android application development.

Let’s dive in….

Architecture pattern

Architecture is an integral part of any application development. It helps in writing a clean , testable and manageable code. Following an architectural pattern will benefit in adding new features and removing an existing one. 

When it comes to android architecture patterns there are several patterns available.

  1. MVC – Model view controller
  2. MVP – Model view presenter
  3. MVVM – Model view viewModel

Each architecture has its own pro’s and con’s. In this blog we are going to see about MVVM architecture patterns and how we can implement them in our application.

Model – Model is the layer responsible for fetching and caching data from a local or remote database servers and providing data to the viewModel layer.

View – As the name says, it represents the UI where the user interacts with the application.

ViewModel – Viewmodel is a layer acts as a communication bridge between view and the model layer. The main objective of viewModel layer is to get data from model layer and provide it to view layer. And it will contain all the required business logics.

Advantages

  1. It will modularise the application
  2. Easy to test
  3. Easy to scale
  4. Separates UI from business logics
  5. Easy to maintain

Disadvantages

  1. Migrating from existing pattern will be difficult.
  2. New developers might find it difficult to start with.

In this architecture diagram you can see that modules are separated in a way that each modules depends only on modules one level below.

The arrow show how the modules will communicate

Activity and fragment will tell the viewModel about the user action.

ViewModel will do the business logic based on user actions. And get the data to be displayed from the repository. 

Livedata can be used to notify the view on data change or it can be binded with view to update the data.

Next the repository layer is part of the model layer in mvvm pattern and it will be responsible to fetch data from the db and remote data sources.

We should not break this flow of communication. The activity or fragment should not get data directly from the repository.

Now let’s see each how we can implement MVVM pattern in android application.

ViewModel

In android, jetpack library provides viewModel class. which can be used to store UI related data in a lifecycle conscious way. The viewModel class allows the data to survive orientation changes.

Another advantage of using viewModel is we can bind the viewModel with the view with the help data binding. This will reduce lots of boilerplate code inside the activity/fragment like finding the views and updating the views whenever the data is changed.

It also provides scope to use kotlin coroutines to make asynchronous calls.

It can be used as a communication bridge between activity and fragment.

And finally we can able to write unit test cases for our business logics.

Now let’s see how we can integrate viewModel in android application.

There are two types of viewModels we can use.

1. viewModel
2. AndroidViewModel(application)

//ViewModel class

class LoginActivityViewModel : ViewModel() { ... }

// AndroidViewModel requires application to initialize.

class LoginActivityViewModel(application : Application) : AndroidViewModel(application) { ... }

Next we can use viewModelProviders to create an instance of the view model for that activity or fragment.

To create instance of viewModel for activity & fragment

// In Activity
mViewModel = ViewModelProvider(this).get(LoginActivityViewModel::class.java)

// In Fragment
mViewModel = ViewModelProvider(viewLifecycleOwner).get(LoginActivityViewModel::class.java)

View models can also be used to pass data between activities and fragments.

Inside the fragment we can get the view model instance of the activity. This way the activity and fragment can pass data between them.

// In Fragment
mViewModel = ViewModelProvider(requireActivity()).get(LoginActivityViewModel::class.java)

Fragment kotlin extension provide another way to get the instance of viewModel.

// build.gradle
implementation 'androidx.fragment:fragment-ktx:1.3.6'
// In Activity
private val mViewModel: LoginActivityViewModel by viewModels()

//In Fragment 
private val mViewModel: LoginActivityViewModel by viewModels()

private val mViewModel: LoginActivityViewModel by activityViewModels()

Next we need a way to communicate or send data to the view layer. For that we can use live data.

// Inside viewModel
val errorMLD = MutableLiveData<String>()

//Inside activity/fragment
mViewModel.errorMLD.observe(this) {
   if (it == null) {
     return
   }
   Toast.makeText(this, it, Toast.LENGTH_SHORT).show()
   mViewModel.errorMLD.value = null
}

Live data can be observed inside the activity / fragments. Based on the data changes we can update the view or perform actions.

Live data also simplifies the way we update the view when it is combined with view and data binding.

We can directly bind the viewModel with the view to update the view data automatically.

First we need to enable the viewBinding and Data Binding.

plugins{
 ... 
 id 'kotlin-kapt'
}

android {
  ...
  buildFeatures {
   viewBinding true
   dataBinding true
 }
}

ViewBinding

ViewBinding will create a Binding class for all the layout xml files.

class LoginActivity : AppCompatActivity() {
   private lateinit var mBinding: ActivityLoginBinding
   private val mViewModel: LoginActivityViewModel by viewModels()
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       mBinding = ActivityLoginBinding.inflate(layoutInflater)
       setContentView(mBinding.root)
   }
}

In the above code you can see that a binding class ActivityLoginBinding for activity_login.xml file is created.

Using this we can access the view. It reduce writing lot of boilerplate code like using findViewById to access the view inside activity/fragments.

DataBinding
Next using data binding we can bind the view model to the view.

To do that, we need to set the root with layout and declare a variable for the viewModel in the layout file.

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <data>
        <variable
            name="viewModel"
            type="com.appmetry.androidpatterns.ui.login.LoginActivityViewModel" />
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical"

Next we need to bind the viewModel with the view by setting the layout view model variable with the viewModel instance we created inside the activity/fragment.

class LoginActivity : AppCompatActivity() {
    private lateinit var mBinding: ActivityLoginBinding
    private val mViewModel: LoginActivityViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mBinding = ActivityLoginBinding.inflate(layoutInflater)
        setContentView(mBinding.root)

        initViewModel()
    }

    private fun initViewModel() {
        mBinding.lifecycleOwner = this
        mBinding.viewModel = mViewModel
    }
}

Now we can use the variable inside the xml file to bind the live data to the views and handle user actions.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="viewModel"
            type="com.appmetry.androidpatterns.ui.login.LoginActivityViewModel" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical"
        android:padding="16dp"
        tools:context=".ui.login.LoginActivity">

        <EditText
            android:id="@+id/email_et"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/enter_email_address"
            android:inputType="textEmailAddress"
            android:text="@={viewModel.emailMLD}" />

        <EditText
            android:id="@+id/password_et"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:hint="@string/enter_password"
            android:inputType="textPassword"
            android:text="@={viewModel.passwordMLD}" />

        <Button
            android:id="@+id/login_btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
android:enabled="@{viewModel.isLoginEnabled}"
            android:layout_marginTop="16dp"
            android:onClick="@{()-> viewModel.login()}"
            android:text="@string/login" />
    </LinearLayout>
</layout>

We can use one way and two way binding to bind viewModel live data to the views.

One way binding “@{}” is generally used to update view based on the live data value.

Two way binding “@={}” is used to get input from the user and update the live data.

Model Layer

Model layer is responsible for fetching and caching data from local db and remote servers. Repository is part of the model layer and it can fetch data from multiple sources. It will return the required data to the viewModels.

We will see extensively on how to create and use repository in the upcoming feature blogs.

Hope this blog helps you in understanding the MVVM architecture and how we can use this in our android application. Check out our other blogs to know more about android best practices in android.

Happy Learning
Team Appmetry

Leave a Reply