viewmodel

ViewModel and ViewModel Factory in Android with Examples

This article will cover the internals of ViewModel and ViewModel Factory, which is a component of the Android Architecture Components. We will first go over the various uses of ViewModel in Android and then we will go into detail about how ViewModel actually works and how it retains itself on configuration changes.

What is ViewModel?

The ViewModel class is used to store and manage user interface data in a lifecycle-aware manner. It allows data to survive screen rotations and other configuration changes. It also enhances the implementation of the MVVM (Model-View-ViewModel) design pattern, which is Google’s recommended Android app architecture for Android applications.

Its dependency

implementation(“androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0-alpha03”)
  • The lifecycles of UI controllers such as activities and fragments are managed by the Android framework. The framework may decide to destroy or recreate a UI controller in response to user actions or device events over which you have no control.
  • Any temporary UI-related data you store in UI controllers is lost if the system destroys or recreates them. For example, one of your app’s activities could include a list of users. When an activity is re-created to accommodate a configuration change, the new activity must re-fetch the list of users. For simple data, the activity can use the onSaveInstanceState() method and restore its data from the bundle in onCreate(), but this approach is only appropriate for small amounts of data that can be serialized then deserialized, not potentially large amounts of data such as a list of users or bitmaps.
  • Another issue is that UI controllers frequently need to make asynchronous calls, which can take a while to finish . To avoid potential memory leaks, the UI controller must manage these calls and make sure that the system cleans them up after they’re destroyed. This management requires tons of maintenance, and re-creating the thing for a configuration change may be a waste of resources because the thing may need to reissue calls it’s already made.

Activities and fragments are UI controllers that are specifically designed to display UI data, respond to user actions, or handle operating system connectivity, such as permission requests. Making UI controllers responsible for data loading from a database or network brings bloat to the class. Excessive responsibility for UI controllers can result in a single class attempting to handle all of an app’s work on its own rather than delegating work to other classes. Assigning excessive responsibility to UI controllers in this manner also makes testing much more difficult.

Download: Learn Java Tutorials Free & Get Certification.

learn java tutorials
Learn Java Tutorials

Advantages

  • Handle configuration changes: When an activity is recreated due to configuration changes, ViewModel objects are automatically retained.
  • It’s objects are also aware of their lifecycle. When the Lifecycle they are watching is permanently destroyed, they are automatically cleared.
  • ViewModels make it simple to share data between fragments in an activity.
  • Support for Kotlin-Coroutines: It includes Kotlin-Coroutine support. As a result, they are easily integrated into any asynchronous processing.

The lifecycle of a ViewModel

ViewModel objects are bound to the Lifecycle passed to the ViewModelProvider when the ViewModel is obtained and It remains in memory until the Lifecycle to which it is scoped is permanently removed, in the case of an activity, when it completes, and in the case of a fragment, when it is detached.

Given below figure shows the various lifecycle states of an activity as it rotates and then is completed. The illustration also depicts the ViewModel’s lifetime alongside the associated activity lifecycle. The states of activity are depicted in this diagram. The same fundamental states apply to a fragment’s lifecycle.

ViewModel

When the system calls an activity object’s onCreate() method for the first time, you usually request a ViewModel. The system may call onCreate() several times during the lifetime of an activity, for instance, when a device screen is rotated. The ViewModel exists from the time you request it until the activity is completed and destroyed.

Example – Without ViewModel

Get Started:

Step 1: Open the activity main.xml file.

Navigate to the app > res > layout > activity main.xml file and paste the code given below into it. The code for the activity main.xml file is shown below.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<TextView
android:id="@+id/counter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="50sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="177dp"
android:text="Button"
android:onClick="increment"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Step 2: Working with the MainActivity.kt file.

Refer to the following code in the MainActivity.kt file. The MainActivity.kt file’s code is shown below.

package com.sagar.viewmodel

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.TextView

class MainActivity : AppCompatActivity() {
    var count: Int = 0
    lateinit var counter : TextView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        counter = findViewById(R.id.counter)
        setText()

    }

    fun increment(v: View){
        count++
        setText()
    }

    private fun setText() {
        counter.text = count.toString()
    }
}

Output:

Simply click the button five to six times to see the incremented number appear on the screen. Simply rotate your emulator or device now.

You will notice that the number decreases to zero, because rotating the screen, erases the value.

  • In the above image, when our activity is created, the system calls onCreate(), then onStart(), and finally onResume(). However, when we rotate the screen, our activity is destroyed, and the system calls onCreate() and other functions one after the other. As a result of our activity being destroyed, our activity data has also vanished.
  • To solve this issue , we use ViewModel, which retain data even after configuration changes such as screen rotation.

Example – with ViewModel

Get Started:

Step 1: Add this dependencies in the build.gradle file:

// ViewModel
implementation(“androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0-alpha03”)

Step 2: Create the MainViewModel.kt Kotlin class file and add the code given below into it.  

package com.sagar.viewmodel

import androidx.lifecycle.ViewModel

class MainViewModel: ViewModel() {
    var count: Int = 0

    fun increment(){
        count++
     }
}

Step 3: Working with the MainActivity.kt file.

Update the following code in the MainActivity.kt file. The MainActivity.kt file’s code is shown below. 

package com.sagar.viewmodel

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.lifecycle.ViewModelProvider

class MainActivity : AppCompatActivity() {
    lateinit var mainViewModel: MainViewModel

    var count: Int = 0
    lateinit var counter : TextView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
        counter = findViewById(R.id.counter)
        setText()

    }

    private fun setText() {
        counter.text = mainViewModel.count.toString()
    }

    fun increment(v: View){
        mainViewModel.increment()
        setText()
    }
}

Run the application and you see that even after rotating the screen we get the same value.

Output:

What is ViewModel Factory?

The factory is responsible to create your instance of ViewModel. If your ViewModel has dependencies and you want to test your ViewModel then you should create your own ViewModelProvider. Factory and passed dependency through ViewModel constructor and give value to the ViewModelProvider.

View model factory

Why do we use ViewModel Factory?

We’ve seen how objects survive configuration changes using ViewModel (On Screen-Rotate), but we didn’t create ViewModel on our own because ViewModelProviders instantiate ViewModels with no-arg constructors by default.

So, if I have a ViewModel with multiple arguments, I need to use a Factory that I can pass to ViewModelProviders to use when an instance of ViewModel is required.

What is the difference between AndroidViewModel and ViewModel?

The difference between the ViewModel and the AndroidViewModel class is that the AndroidViewModel one provides you with an application context, which you need to provide when you create a view model of the type AndroidViewModel.

AndroidViewModel is a subclass of ViewModel that is aware of the Application context. However, having access to a context can be dangerous if you aren’t observing or reacting to the context’s lifecycle. It is best to avoid dealing with objects that have a lifecycle in ViewModels.

Why do we need ViewModel Factory?

  • We are unable to create ViewModel on our own. To create ViewModels, we need the ViewModelProviders utility provided by Android.
  • ViewModelProviders, on the other hand, can only instantiate ViewModels with the no-arg constructor.
  • So, if I have a ViewModel with multiple arguments, I must use a Factory that I can pass to ViewModelProviders to use when an instance of MyViewModel is needed.
  • To create an instance of ViewModel, I need to have a factory that ViewModelProviders can use.
  • ViewModelProviders Utility is unable to create a ViewModel instance with an argument constructor because it does not know how or what objects to pass in the constructor.

For Example:

If we need to pass some input data to the ViewModel constructor, we must create a factory class for it.


class MainViewModelFactory(val counter: Int) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MainViewModel(counter) as T
}
}

We cannot directly create the object of the ViewModel as it would not be aware of the lifecycle owner. So we use:

mainViewModel = ViewModelProvider(this,MainViewModelFactory(5))[MainViewModel::class.java]

Example using ViewModel Factory

Now we are going to add ViewModelFactroy to the example given above to pass some input data to the ViewModel constructor, we must create a factory class for it.

Get Started by continuing the above example:

Step 1: Create a MainViewModelFactory and add the given below code into it.

 package com.sagar.viewmodel

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

class MainViewModelFactory(val counter: Int) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return MainViewModel(counter) as T

    }
}

Step 2: The MainViewModel looks like this:

package com.sagar.viewmodel

import androidx.lifecycle.ViewModel

class MainViewModel(val value: Int): ViewModel() {
    var count: Int = value

    fun increment(){
        count++
     }

}

Step 3: In MainActivity.kt file– Add the MainViewModelFactory and pass the 5 as an argument in ViewModelProvider. the code looks like this:

package com.sagar.viewmodel

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.view.Window
import android.view.WindowManager
import android.widget.TextView
import androidx.lifecycle.ViewModelProvider

class MainActivity : AppCompatActivity() {
    lateinit var mainViewModel: MainViewModel

    var count: Int = 0
    lateinit var counter : TextView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mainViewModel = ViewModelProvider(this,MainViewModelFactory(5))[MainViewModel::class.java]
        counter = findViewById(R.id.counter)
        setText()


    }

    private fun setText() {
        counter.text = mainViewModel.count.toString()
    }
    fun increment(v: View){
        mainViewModel.increment()
        setText()
    }
}

Output:

When you run the application and you will get the default value of five.

That’s all about the AndroidViewModel.

If you are interested in learning Data binding and View Binding, you can go through Data Binding in Android with Example, and View Binding in Android with Example here I have explained Data Binding, One-way, and Two-way Data Binding, @BindingAdapter, View Binding in detail with suitable Examples in Android, and several scenarios in which data and view binding can be useful. and I assure you that by the end, you will have a clear understanding of this commonly used library.

  • We hope that this guide will assist you in understanding all the concepts of Android ViewModel and ViewModel Factory in android. We have concentrated on making basic, meaningful, and easy-to-learn concepts of it and Factory with suitable examples. Still, if you have any problems regarding it, please post them in the comments section, we will be glad to assist you.

This Post Has 5 Comments

  1. Stephan

    Lovely explanation! Thank you!

  2. Deva

    passing parameter to viewmodel was the interesting touch. nice

    1. Developers Dome

      We appreciate your time and effort in sharing your valuable feedback.

Leave a Reply