Published on

Android ViewModels Explained!

What do you get when you combine the concept of a view and a model? A ViewModel of course! In this blog post we will be giving a detailed explanation for what a ViewModel is so let's get into it!

Diagram of the MVVM Architecture!

Figure 1: Diagram of the MVVM Architecture!🚀

What is a ViewModel and why use ViewModels?

Put in simple terms, the ViewModel is a class designed to hold data related to the UI in a way that takes the Android Activity lifecycle into account. The main idea behind ViewModels is that they allow data to survive changes to the Android configurations. These configurations changes can include changes in screen orientation, keyboard availability and when users enable multi-window mode(more on multi-window mode can be found in this Android Developers article).

Traditionally, whenever one of the aforementioned configuration changes occur the current Activity of the application would be destroyed and recreated. Therefore, all of state and UI data relating to that Activity would be lost. Thanks to ViewModels this deficiency can be overcome quite easily!

A Simple Example

Here is an example of what a ViewModel class would look like:

class ExpenseEditViewModel @Inject constructor(
    private val db: Database
) : ViewModel() {
    private var expense: Expense? = null

    fun initWithDateAndExpense(date: Date, expense: Expense?) {
        this.expense = expense
    }
}

Before explaining anything, note that we will be using Kotlin as our language of choice for this example.

The main requirement to creating a ViewModel is to extend the ViewModel class, which you can see in the example above.

Next, the @Inject annotation is used to inject a Database dependency in the constructor(read this great Medium article to learn more about dependency injection in Kotlin)

Finally in order to use this ViewModel, an Activity class must use it like so:

import com.coding.informer.net_worth_calculator.models.viewmodels.ExpenseAddViewModel
import androidx.activity.viewModels

class ExpenseAddActivity : AppCompatActivity() {

    private val viewModel: ExpenseAddViewModel by viewModels()
    ...
    ...
    ...
}

Some ViewModel Rules

Note that there are some rules when using ViewModels, with the main one being: Do no reference View classes using your ViewModels. This is because the chance of memory leaks when referencing Views in ViewModels is very high. Therefore, it is important to build ViewModels so that they contain almost all of the state, presentation and behavior logic that is obtained from a provided View.

That being said, the View will have some minor binding to the ViewModel. For this binding to occur observable fields are used in most cases(To learn more about observable fields click here to visit its Android Developers page)

Here is a code example where a View class is binding to a ViewModel class using the Observer interface:


class SampleView : Fragment() {

    private lateinit var viewModel: LoginViewModel

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_sample, container, false)
    }


    //Create and bind to ViewModel
    override fun onStart() {
        super.onStart()
        viewModel = ViewModelProvider(
            this,
            LoginInjector(requireActivity().application).provideUserViewModelFactory()
        ).get(LoginViewModel::class.java)

        //start background anim
        (root_fragment_login.background as AnimationDrawable).startWithFade()

        setUpClickListeners()
        observeViewModel()

        viewModel.handleEvent(LoginEvent.OnStart)
    }

  private fun observeViewModel() {
        viewModel.signInStatusText.observe(
            viewLifecycleOwner,
            Observer {
                //"it" is the alue of the MutableLiveData object, which is inferred to be a String automatically
                lbl_login_status_display.text = it
            }
        )

        viewModel.authButtonText.observe(
            viewLifecycleOwner,
            Observer {
                btn_auth_attempt.text = it
            }
        )
    }

}

As you see here, the onStart() Android lifecycle method uses dependency injection to inject the viewModel variable with an instance of LoginViewModel. After that, the observeViewModel() method is triggered in order to attach Observer instances to observe the ViewModel for changes.

Another rule that should be mentioned in that a given ViewModel should only be used for one View or Activity only. If more Views/Activities need a similar ViewModel then create new ViewModels would be the best course of action. This is because when we call VewModelProvider(this, ...).get(...) a ViewModelStore instance is created which is bound to this. Therefore, different Views/Activities have different ViewModelStores and each of them create a different instance of the ViewModel which leads to us not being able to have the same instance of a given ViewModel in multiple ViewModelStores.

Now if you really wanted to, you could use the same ViewModel instance in multiple Activities by maintaining that ViewModel instance in a separate architecture layer such as the Data layer. In the Data layer you could create a Repository class that would use the Singleton design pattern(to learn more about the Singleton design pattern read this sourcemaking.com article) to hold the ViewModel instance. Here is a code snippet for further clarification:

public class ExpenseEditViewModel extends ViewModel {
    // Create a LiveData with a String
    private MutableLiveData<String> mCurrentData;

    public MutableLiveData<String> getCurrentData() {
        if (mCurrentData == null) {
            mCurrentData = ExpenseRepository.getInstance().getCurrentData();
        }
        return mCurrentData;
    }
}

//Singleton Repository class
public class ExpenseRepository

    private MutableLiveData<String> mCurrentData;

    public MutableLiveData<String> getCurrentData() {
        if (mCurrentData == null) {
            mCurrentData = new MutableLiveData<>();
        }
        return mCurrentData;
    }
    ...
}

Note that when following the design pattern detailed above, if the user exits the app the View instance will be destroyed accordingly. This means that the ViewModel that was being used by that View would not be observed anymore and therefore our singleton repository class would not be destroyed until the whole application process was manually killed. In other words, because our repository class holds a reference to the ViewModel instance, that ViewModel instance would cause a memory leak.

And with that ends our post on Android ViewModels. There is a lot that couldn't be covered but hopefully this gives you a basic idea of what ViewModels are and why they are used.

Stay tuned for another blog post where we go over the MVVM Architecture, it is sure to be very informative!🚀

Conclusion

Thanks again for reading this blog post on Android ViewModels!

Well that's it for this post! Thanks for following along in this article and if you have any questions or concerns please feel free to post a comment in this post and I will get back to you when I find the time.

If you found this article helpful please share it and make sure to follow me on Twitter and GitHub, connect with me on LinkedIn and subscribe to my YouTube channel.