How ViewModels Work on Android

Learn how to use a ViewModel and how it works internally

Satya Pavan Kantamani
Feb 2 · 7 min read
Photo by Bill Oxford on Unsplash

According to Android’s documentation:

“Android architecture components are a collection of libraries that help you to build robust, testable, and maintainable apps.”

Android architecture components have many things, including classes for “managing your UI component lifecycle and handling data persistence,” as the docs say. In this piece, let’s learn about ViewModel and its usages.


Introduction

ViewModel is a class that’s responsible for preparing and managing the data for a UI component (an activity or a fragment). It also provides an easy way for communication between the activity and the fragment or between different fragments.

ViewModel is always created in association with a scope (a fragment or an activity) and will be retained as long as the scope is alive — i.e.., if it’s an activity until it’s finished. In other words, this means a ViewModel won’t be destroyed if its owner is destroyed for a configuration change (e.g., a rotation). The new instance of the owner will just reconnect to the existing ViewModel. Lets’s see how this happens

ViewModel Overview via the Android docs

The purpose of the ViewModel is to acquire and keep the information that’s necessary for an activity or a fragment. The activity or the fragment should be able to observe changes in the ViewModel. ViewModels usually expose this information via LiveData or Android Data Binding.

ViewModel’s only responsibility is to manage the data for the UI. It should never access your view hierarchy or hold a reference back to the activity or the fragment.

Let’s first understand the benefits of using ViewModel.

Benefits

  1. ViewModel survives rotation and other configuration changes.

2. ViewModel keeps running while the activity is on the back stack.

3. ViewModel is lifecycle-aware.

4. ViewModels, with the help of LiveData, reactively perform the UI changes. Whenever the data is changed, the UI gets updated based on observing the LiveData with current data inside the ViewModel.

5. ViewModel is easy to understand and easier for testing.

In this piece, we’ll dive into three important aspects of the ViewModel.

1. Creating and using a simple ViewModel.
2. Creating and using a ViewModel with arguments using ViewmodelProvide.Factory.
3. Shared ViewModels for communication between the activity and the fragment.


Simple ViewModel

Let’s better understand ViewModels by creating a simple example.

Example

There are four main steps in creating and using a ViewModel:

  1. Add dependencies in your app-level build.gradle.
  2. Separate out all your data from your activity by creating a class that extends the ViewModel.
  3. Create a ViewModel instance in your activity to use it.
  4. Set up communications between your ViewModel and your View layer.

Add dependencies

To use the ViewModel, we need to add dependencies into our app/build.gradle file.

implementation "android.arch.lifecycle:extensions:1.0.0"
annotationProcessor "android.arch.lifecycle:compiler:1.0.0"

Or if you’re using Kotlin with AndroidX, then add:

implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'

Creating a ViewModel class

Let’s create a SampleViewModel class for the activity without any data. This can be done simply by extending the ViewModel class.

package com.example.viewmodel

import androidx.lifecycle.ViewModel

class SampleViewModel :ViewModel() {

override fun onCleared() {
super.onCleared()
// Dispose All your Subscriptions to avoid memory leaks
}
}

onCleared(): This method will be called when this ViewModel is no longer used and will be destroyed. It’s useful when the ViewModel observes some data, and you need to clear this subscription to prevent a leak of this ViewModel.

Creating a ViewModel instance in your activity

Create a ViewModel instance in your activity using ViewModelProvider. We need to pass the context and ViewModel class name to ViewModelProvider to get the instance.

package com.example.viewmodel

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.ViewModelProvider

class MainActivity : AppCompatActivity() {

lateinit var viewModel : SampleViewModel

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProvider(this).get(SampleViewModel::class.java)

}
}

ViewModelProvider is a utility class for ViewModelStore. It internally refers to ViewModelStore to return an existing instance of the ViewModel if it exists — otherwise, it creates a new one. ViewModelStore internally keeps track of the ViewModels using a HashMap.

Setting up communication between the ViewModel and the activity

Now let's see how to get the values from the ViewModel and show them in the View layer. Let’s create a LiveData inside the ViewModel, and on the click of a button inside, the activity updates its value to show a toast message.

Now let’s handle the UI-layer code inside the activity.

Whenever the data changes in the background for badgeCount through LiveData, we receive a callback inside the Observer we registered. So the UI will always be updated with the latest data available.

Now we’re done with basics of the ViewModel and its communication, but there’s much more. Let’s continue.


ViewModelProvider.Factory

As the above ViewModel doesn’t have any arguments inside the constructor, it’s easy to do that.

But if you have an argument you need to pass to the constructor of the ViewModel, then you need ViewModelFactory. If you try the above approach without the factory, then it results in a compile-time error.

class SampleViewModel :ViewModel(name:String) {

override fun onCleared() {
super.onCleared()
// Dispose All your Subscriptions to avoid memory leaks
}
}

When you run the app, the the process crashes with an error: java.lang.RuntimeException: Cannot create an instance of class com.example.viewmodel.SampleViewModel.

The ViewModelProvider() method internally creates a default ViewModelProvider.Factory implementation for creating our ViewModel with no arguments. So when you add an argument in the constructor, the inner implementation of the ViewModelProvider.Factory failed to initialize your ViewModel because ViewModelProvider.Factory calls the primary constructor for creating the ViewModel’s instance.

We need to use ViewModelProvider.Factory to create your ViewModel instance with custom arguments. If there are no arguments, then there’s no need for a custom factory.

ViewModelProvider.Factory is an implementation of the factory interface that’s responsible for instantiating ViewModels. We need to create our own factory to create ViewModel instances with arguments

The simplest way of doing this is to create our own custom factory-extending ViewModelProvider.Factory interface, which gives an overridden method, create, which is responsible for creating our ViewModel's instance.

package com.example.viewmodel

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider

class SampleViewModelFactory (val arg: String): ViewModelProvider.Factory {

override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return modelClass.getConstructor(String::class.java).newInstance(arg)
}
}

The modelClass.getConstructor(String::class.java) gets the constructor that has type String and creates an instance of ViewModel by calling the newInstance method and passing constructor values to this method.

Now let's check the implementation inside the activity.

class MainActivity : AppCompatActivity() {

lateinit var viewModel: SampleViewModel

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val factory = SampleViewModelFactory("sample")
viewModel = ViewModelProvider(this,factory).get(SampleViewModel::class.java)
}
}

If your ViewModel has dependencies or arguments and you want to create your ViewModel instance, then you should create your custom ViewModelProvider.Factory and pass the dependency or arguments through the ViewModel constructor and give a value to the ViewModelProvider.Factory instance.

val factory = SampleViewModelFactory("sample")
ViewModelProvider(this,factory).get(SampleViewModel::class.java)

Otherwise, we can simply use ViewModelProvider(this) to create the instance.

ViewModelProvider(this).get(SampleViewModel::class.java)

Shared ViewModel for Communication Between the Activity and the Fragment

To communicate between different fragments or between a fragment and an activity, we generally use interfaces or a target fragment.

But if they’re sharing a common ViewModel with an activity scope, then it’d be easier to communicate using the ViewModel. Let’s check out how to do it.

To achieve the above flow, we need to create a ViewModel instance using in activity scope in the fragments and the activity so only one common instance is created and shared across different fragments.

Problem

It’s very common that two or more fragments in an activity need to communicate with each other.

Imagine a common case of two fragments: You have a Fragment1, in which the user selects an item from a list, and Fragment2, displaying the contents of the selected item. This case is never trivial as both fragments need to define some interface description, and the owner activity must bind the two together. In addition, both fragments must handle the scenario where the other fragment is not yet created or visible.

Solution

This common pain point can be addressed by using ViewModel objects. These fragments can share a ViewModel using their activity scope to handle this communication.

Now let’s create the SharedViewModel.

class SharedViewModel : ViewModel() {
val selected = MutableLiveData<String>()
fun selectedItem(item: String) {
selected.value = item
}
}

Use case: Let’s assume there are two fragments — Fragment1 and Fragment2 — and a main activity to which these two fragments are attached. We need to post something from Fragment1 to Fragment2 on a button click.

Fragment1 looks like:

Fragment2 looks like:

Notice both fragments retrieve the activity that contains them. That way, when the fragments each get the ViewModelProvider, they receive the same SharedViewModel instance, which is scoped to the activity.

This approach offers the following benefits:

  • The activity doesn’t need to do anything or know anything about this communication
  • Fragments don’t need to know about each other — besides the SharedViewModel contract. If one of the fragments disappears, the other one keeps working as usual.
  • Each fragment has its own lifecycle and isn’t affected by the lifecycle of the other one. If one fragment replaces the other one, the UI continues to work without any problems.

So ViewModel can be used to ease the communication between UI components.

Better Programming

Advice for programmers.

Satya Pavan Kantamani

Written by

Android Dev @Toppr. Interested in Traveling, App development. Based in Hyderabad, India. Catch me at https://about.me/satyapavankumar

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade