android viewmodel
| | | | |

ViewModel – Android Architecture Component

Couple of years ago, when I’m studying at my University. We have given the assignment to make a color changer app. The app is very basic where we have only one button and when a user clicks the button it changes its background color.

So like I said, the app is very basic and I’ve made the app in just half an hour. When the user presses the button, it perfectly changes its background. But when a user rotates the phone, it vanished the current state of an app and shows a default color instead of the previously shown background color.

So, what happened..? Rotating a phone can cause a configuration change. The configuration changes can cause your whole Activity to tore down and then recreate it. So, to save the current state of the activity we need some kind of UI data holder. As for beginner, I use the onSaveInstanceState trick to store the app state.

The onSaveInstanceState is the best approach to store the small amount of data. Also in my scenario, I only need to store the current background color.

Problem

What happened if I need to store a large amount of data instead of just storing the background color. Let’s say I need to store the list of users and their profile picture when the app going to rotate. In here we cannot use onSaveInstanceState trick because it can only be used to store a small amount of data. Now how we can store the data…?

Thanks to this, at Google I/O 2017 the Android Framework team introduced a new Architecture Component which deals this kind of rotation.

ViewModel

The ViewModel class is designed to store and manage UI-related data in a conscious way. The ViewModel is able to live through the configuration changes. It means that even after the Activity is destroyed and recreated after the phone rotation, you’ll still have the same ViewModel with the same data.

Now instead of storing all of your UI data in your Activity put in the ViewModel. One common pitfall that we all do when developing the Android application is putting a lot of variables, logic, and data into your Activity and Fragment. This creates a large unmaintainable whole mess of class and violating the Single Responsibility Principle.

The Lifecycle of ViewModel

The ViewModel is responsible for preparing and managing the data for an Activity or Fragment. It is always created in the scope of an Activity or Fragment and will be retained as long as the scope is alive. The lifecycle of a ViewModel extends from the associated UI when it’s first created until it is completely destroyed. The following shows the lifetime of a ViewModel associated with Activity lifecycle.

You see the ViewModel exists throughout the activity lifecycle. The onCreate may be called several times through during the lifecycle of an Activity, such as when the app is rotated but the model remains.

So, enough of this theory let’s how we can create a simple ViewModel class.

Creating a ViewModel

To make the view model you end up extending the ViewModel class and then put all your UI related data in the model class, that you gonna show in the Activity or Fragment. So, let’s create a ViewModel for the screen to store the current background color.

The following shows how to create a ColorChangerViewModel.

class ColorChangerViewModel : ViewModel() {
    private var colorResource: Int = 0xfff

    fun setColorResource(colorResource: Int) {
        this.colorResource = colorResource
    }

    fun getColorResource() = colorResource
}

You see our ViewModel is only responsible for holding the data that you’re gonna show in the UI. In our case, our ViewModel only needs to know about the color.

Note: ViewModel should not contain elements, that contains a reference to the UI such as Views since this will create an indirect reference to a Context. It will also cause a memory leak.

Now you guys must be thinking if I need Context inside ViewModel to get the SystemService or any other logic you wanna do with it. So, thankfully we’ve AndroidViewModel which is simply a ViewModel that includes an Application reference. Storing an Application Context inside ViewModel is ok because an application context is tied to the application lifecycle.

So, we create ViewModel let’s see how we gonna use this view model inside the activity.

MainActivity

For creating a ViewModel instance you use the utility class called ViewModelProviders.

class MainActivity : AppCompatActivity() {

    private lateinit var colorChangerViewModel: ColorChangerViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        colorChangerViewModel = ViewModelProviders.of(this).get(ColorChangerViewModel::class.java)
        mainActivityRootView.setBackgroundColor(colorChangerViewModel.getColorResource())
        changeColorButton.setOnClickListener {
            val color = generateRandomColor()
            mainActivityRootView.setBackgroundColor(color)
            colorChangerViewModel.setColorResource(color)
        }

    }

    private fun generateRandomColor(): Int {
        val rnd = Random()
        return Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256))
    }
}

Notice the ViewModelProviders takes an activity instance. This is the mechanism that allows you to rotate the device to get a technically new activity instance but always ensures that activity instance is associated with the same ViewModel.

You see here our activity is only responsible for knowing how to draw that data to the screen and receiving user interactions but not for processing them. Our ViewModel instance, we’re accessing the getter and setter to save and retrieve the color.

Every time the user presses a button it updates the previous color with the new color in ViewModel. So, when the user rotates the app it destroys the activity and again calls the onCreate method which sets previous background color instead of white default color.

When creating an instance of a ViewModel with ViewModelProviders.of(this).get(SomeViewModel::class.java). In this scenario, we have a default constructor, what if we need the parameterize constructor for our ViewModel. For this kind of ViewModel, we need to create the custom ViewModelProvider.Factory.

Creating ViewModelProvider.Factory

To create a custom model factory we need to implements the ViewModelProivder.Factory interface. The custom model factory is responsible to instantiates the ViewModels.

The following shows how to create ViewModelProvider.Factory.

class CustomViewModelFactory(private val user: User) : ViewModelProvider.Factory {

    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return UserViewModel(user) as T
    }
}

You see in create method we’re calling our parameterize ViewModel constructor. Let’s see the UserViewModel class.

class UserViewModel(val user: User) : ViewModel(){
    .......
    .......
}

Now we have the model factory and ViewModel class let’s see how we can use this in the Activity.

class MainActivity : AppCompatActivity(){
       ......
       ......
       override fun onCreate(savedInstanceState: Bundle?) {
             .......
             .......
             val factory = CustomViewModelFactory(someUserModel)
             val userViewModel = ViewModelProviders.of(this, factory).get(UserViewModel::class.java)
       }
}

When creating a view model we need to pass the Activity reference along with ViewModelProvider.Factory instance.

Removing ViewModel Lifecycle Forwarding

At some point, you may need to clean up the view model. The idea is that you may want to delete or remove the data in the ViewModel on configuration change. Simply override the onCleared method in the view model class. You can also cancel a long-running task in the onCleared method.

override fun onCleared() {
    super.onCleared()
    cleanUpSomeBackgroundWork()
}

The onCleared method will call when the Activity completely gets destroyed.

ProTip

The ViewModel class is also designed to work well LiveData and Data Binding. Using all of these together you can create a Reactive UI which is just a fancy way of saying a UI that automatically updates whenever the underlying data changes.

Alright, this is my demonstration about how to simply create ViewModel and work with it. I hope you guys have learned something from this post. If you have question or comments regarding this post please do comment below.

Thank for being here and keep reading…

Similar Posts

3 Comments

  1. @naresh It’s becuase the ViewModel class didn’t have the access to your View. Also it’s very bad approach to pass the View inside ViewModel.

Comments are closed.