A Simple View Of ViewModels

Shashank Yadav
8 min readJun 28, 2023

Introduction

While building applications, many of you developers have probably encountered the problem of losing your application’s data when you rotate your device. Ever wondered why that happens? Well, it’s because your activity is recreated when there is a configuration change, And when your activity is recreated, your data or state is lost. But fear not, ViewModels are here to the rescue!

ViewModels are not exclusive to Android applications, but they have a special place on Android.

Prerequisites

  • Basics of Android Development.
  • Understanding of Kotlin.
  • Knowledge of LiveData and DataBinding.
  • Knowledge of Activity Lifecycle.

What is application architecture?

Before we move on to ViewModels, let’s learn a little about app architecture.

When we build applications, we want to create a codebase that is consistent, scalable, maintainable, and understandable. To achieve this, we typically use architectural design patterns. Some popular design patterns include MVVM, MVI, and MVC. These patterns outline how we should structure our apps.

Your app may be scaled and expanded with new features in the future with the help of a well-designed app architecture. Moreover, it facilitates teamwork. The separation of concerns and UI driving from a model are the most popular architectural ideas.

In Android, MVVM is one of the most popular design patterns. MVVM stands for Model View View Model, and the ViewModel is a key part of the MVVM design pattern. While we don’t need to dive into the details of MVVM at this moment, it’s important to understand its significance in the development process.

You can learn more about app architecture here.

What is ViewModel?

In technical language, A class called the ViewModel is responsible for managing and storing the data for an Activity or Fragment. It manages an app’s data and enables it to keep to the architecture principle of drawing the user interface (UI) from the model.

In layman's language, ViewModel is a place that contains the business logic of our application which is the real program logic that isn’t just updating a UI element for example filtering a list or checking an user input.

Why should we use ViewModels?

without viewmodel

Alright, now take a quick look at the gif a couple of times and see what’s going on there. You got it right the text color changes when you click the button, but when you rotate the screen, it’s a different story ( That’s because the activity is recreated on configuration change, and the text color is not persisted. ). But don’t worry, ViewModels are here to save the day!

When the configuration is changed, the ViewModels are automatically saved so that the data it contains is immediately accessible to the following Activity or Fragment instance. It also means that whenever you switch between activities or make setup adjustments, like rotating the screen, your UI won’t need to request data again.

The ViewModels also contain the state of the specific screen. The state is essentially how our UI looks at a given moment. It is a value that can change over time. For example, if the background color of your app changes over time, that is a state. You can learn more about state here.

with ViewModel

After applying ViewModel to our application, the text color is so attached to the screen that it won’t let go even if you rotate it.

Now, you may ask if there isn’t some other way to save state or configuration changes well there is by using onSavedInstanceState() Callback but it has its own drawbacks -:

  • It requires you to write extra code to save the state in a bundle.
  • the onSavedInstanceState() callback is only called if the activity is recreated.
  • Stored data in a bundle is minimal.
  • You have to implement logic to retrieve that state.

ViewModels offer several advantages over other methods for saving state, and they can help you write more modular, maintainable, and efficient code. One of their biggest advantages is that they are lifecycle-aware.

How to use ViewModels?

To understand how ViewModels work, we’ll create the same color-changing text application.

To get started, create a new project in Android Studio. Select Empty Activity and click Next.

Select New Project
Select Empty Activity

After clicking “Next”, name your application ViewModelExample (or anything else you like). Select Kotlin as the language and click Finish.

Your Gradle build must have finished by now, and you should be in the MainActivity.kt file.

Open the build.gradle file for your app module and add the following code to the android block:

 buildFeatures{
dataBinding = true
}

In addition, add the following dependencies to your app module:

dependencies{
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1'
implementation 'androidx.fragment:fragment-ktx:1.6.0'
}

These dependencies are required to use ViewModels and LiveData in your app. Once you have added these changes, you can sync your project.

Add the following XML code to your activity_main.xml file in the res/layout directory:

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

<data>

<variable
name="viewModel"
type="com.shanks.viewmodelexample.MainViewModel" />

</data>

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"
tools:context=".MainActivity">

<TextView
android:id="@+id/tvMyName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Hi, I Am Shashank"
android:textColor="@{viewModel.currentColor}"
android:textSize="40sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/btnChange"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:padding="10dp"
android:text="Change"
android:textAlignment="center"
android:textSize="25sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Now, right-click on the com.example.viewmodelexample package and select New > Kotlin Class. Name the class MainViewModel and click OK.
Change MainViewModel to be subclassed from ViewModel. ViewModel is an abstract class, so you need to extend it to use it in your app.

class MainViewModel : ViewModel() {
//code...
}

To link your MainViewModel to your UI controller i.e. your activity or fragment, create a reference (object) to theViewModel inside the MainActivity.

  • At the top of MainActivity class, add a property of the type MainViewModel.
  • Initialize the MainViewModel using the by viewModels() Kotlin property delegate. In short, Property delegation in Kotlin is like hiring a personal assistant to take care of your getter-setter responsibilities. You can focus on other things, like writing code, while your assistant takes care of the tedious details. If you want to learn more about Kotlin delegate click here.
 private val viewModel : MainViewModel by viewModels()

In your app, if you initialize the view model using the default MainViewModel constructor, like below:

private val viewModel = MainViewModel()

Then the app will lose the state of the viewModel reference when the device goes through a configuration change. For example, if you rotate the device, then the activity is destroyed and created again, and you'll have a new view model instance with the initial state again. That’s why we should use the property delegate approach.

Now, add the following code in your MainViewModel.kt

// List of colors 
private val colorList = listOf(
Color.rgb(255, 0, 0),
Color.rgb(0, 0, 255),
Color.rgb(0, 255, 0),
Color.rgb(255, 255, 0),
Color.rgb(0, 0, 0),
Color.MAGENTA,
Color.CYAN
)

// Declare private mutable LiveData object that can only be modified
// within the class it is declared.
private val _currentColor = MutableLiveData<Int>()

// Declare another public immutable LiveData and override its getter method.
// Return the private property's value in the getter method.
// So, It can be accessed outside the current class.
val currentColor : LiveData<Int>
get() = _currentColor

// Logic of application

init {
getColor()
Log.d("viewModel", "in-it")
}

// getcolor() function sets the _currentColor value to a random color.
fun getColor() {
_currentColor.value = colorList[getRandomValue(0, colorList.size)]
}

// This function returns a random number between 0 to size of list.
private fun getRandomValue(start : Int, end: Int): Int {
return Random.nextInt(start, end);
}

The above code uses the concept of LiveData and backing property. To learn more about LiveData, click here.

Now, let’s add the logic to our app so that when the button is clicked, the color changes. To do this, we’ll add the following code to our MainActivity.kt file. I'm also setting up data binding in the MainActivity file so that we can interact with the XML file.

// Your MainActivity.kt should look like this

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.viewModels
import androidx.databinding.DataBindingUtil
import com.shanks.viewmodelexample.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

private val viewModel : MainViewModel by viewModels()
private lateinit var binding : ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

binding.viewModel = viewModel

// Using the viewModel object to call the getColor() function
binding.btnChange.setOnClickListener {
viewModel.getColor()
}

// Specify the activity as the lifecycle owner of the binding.
// This is used so that the binding can observe LiveData updates
binding.lifecycleOwner = this

}
}

And your MainViewModel.kt should look like this.

import android.graphics.Color
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import kotlin.random.Random

class MainViewModel : ViewModel() {

private val colorList = listOf(
Color.rgb(255, 0, 0),
Color.rgb(0, 0, 255),
Color.rgb(0, 255, 0),
Color.rgb(255, 255, 0),
Color.rgb(0, 0, 0),
Color.MAGENTA,
Color.CYAN
)


private val _currentColor = MutableLiveData<Int>()

val currentColor : LiveData<Int>
get() = _currentColor

init {
getColor()
Log.d("viewModel", "in-it")
}

fun getColor() {
_currentColor.value = colorList[getRandomValue(0, colorList.size)]
}

private fun getRandomValue(start : Int, end: Int): Int {
return Random.nextInt(start, end);
}



}

Great! We’ve completed our application using ViewModel. Now, let’s run it on any virtual device or on our physical device. We’ll see that the color of the text remains the same even after we rotate the device.

Final Application

Conclusion

In this post, we explored the ins and outs of ViewModels. So, now we know

  • What are ViewModels?
  • Why should we use ViewModels?
  • How to use ViewModels in an efficient way?

Want to learn more about ViewModels or about Architecture Components then click here.

Also, there are some things that I have not explained in detail in this post, like LiveData and DataBinding. That’s because my main focus was on explaining ViewModels. I hope you understand.

If you have any specific Android topics you’d like me to blog about, or if you have any questions or suggestions, please feel free to leave a comment below. I’m always happy to hear from you!

--

--