Sign in Go Pro

Android Model-View-ViewModel Pattern

Using Architecture Component ViewModels w/ Data Binding Observables

This lesson is for PRO members.

Upgrade today to get access to all the PRO lessons.

Unlock this lesson

Up next



One of the more common issues developers initially face when developing on Android is how to handle orientation changes, device rotation. In Android when the users device rotates, the Activity, Fragments and any Views on screen are destroyed and recreated in order to adjust to the new configuration. The problem is that the state of the UI must be retained somehow when this happens. Android offers an elegant solution to this problem, with something ironically called the ViewModel, which comes as part of a separate library, Architecture Components.

Key concepts in this lesson that you will learn:
* Why the Activity and Views are destroyed on rotation.
* ViewModel and AndroidViewModel, what they are and how to use them to handle rotations in your app.
* The key differences between ViewModel and AndroidViewModel
* How to use within a Data Binding application, making a ViewModel that is both Observable and extends AndroidViewModel.
* Through this, you will have a deeper understanding of how Observability works with Data Binding.



I've noticed that removeOnPropertyChangedCallback in ObservableViewModel never get called. Do we possibly have memory leak here?

Also, when I changed line: mCallbacks.notifyChange(this, BR._all) to mCallbacks.notifyChange(this, BR.vm) in ObservableViewModel, I've noticed that two-way data binding doesn't work, i.e. labels don't get refreshed. What is the reason for that? I thought that when choosing BR.vm would be fine-grained update, suitable for some much bigger project.

Thanks in advance. Great course, by the way, great topic and easy to understand explanations.

Hi there! Great questions! I’ll take each individually here.

removeOnPropertyChangedCallback never gets called?

This actually does get called, eventually and periodically, by the Data Binding framework to clean listeners that have been collected. It’s likely however, that your ViewModel will still have some callbacks registered when it is destroyed, and this is okay. The Data Binding framework uses weak references for the observers and it’s not required that they be unregistered before the ViewModel is destroyed. This won’t cause any memory leaks.

With that said, if you rotate the phone rapidly, several times in a row, while on the same screen. You’ll notice ObservableViewModel.addOnPropertyChangedCallBack is called several times and if you look inside the source for android.databinding.ViewDataBinding, you’ll see the observer count does rise each time.

This is where the periodic removal comes in. If you use the app long enough, rotate a few times, and have a breakpoint set on ObservableViewModel.removeOnPropertyChangedCallback. You’ll see that it is called periodically to clean up old observers and if you look up the call stack you can find more detail about where that comes from, how it’s triggered, etc.

Much of this is internal implementation details of Data Binding and not covered in the course lecture, but these are great questions to ask! Thank you!

Can I notify the View of individual property changes?

If you change the property Id from BR._all to BR.vm none of the properties change (not only the 2-way bound properties). This is because the VM object reference isn’t changing, only some of the properties contained within.

With that said, you can notify the view of an individual property change. To do that, you’d have to annotate any property that you want to notify individually with a @Bindable annotation.

// Inside CalculatorViewModel
// Inputs
@Bindable var inputCheckAmount = ""
@Bindable var inputTipPercentage = ""
// Outputs
@get:Bindable val outputCheckAmount get() = getApplication<Application>().getString(R.string.dollar_amount, lastTipCalculated.checkAmount)
@get:Bindable val outputTipAmount get() = getApplication<Application>().getString(R.string.dollar_amount, lastTipCalculated.tipAmount)
@get:Bindable val outputTotalDollarAmount get() = getApplication<Application>().getString(R.string.dollar_amount, lastTipCalculated.grandTotal)
@get:Bindable val locationName get() = lastTipCalculated.locationName

This generates a BR constant for each property in the form of BR.<propertyName>

For example:

package com.acme.tipcalculator;
public class BR {
public static final int _all = 0;
public static final int inputCheckAmount = 1;
public static final int inputTipPercentage = 2;
public static final int item = 3;
public static final int locationName = 4;
public static final int outputCheckAmount = 5;
public static final int outputTipAmount = 6;
public static final int outputTotalDollarAmount = 7;
public static final int vm = 8;

Then you can notify individual properties that change...

fun notifyChange() {
mCallbacks.notifyChange(this, BR.inputCheckAmount)
mCallbacks.notifyChange(this, BR.inputTipPercentage)
mCallbacks.notifyChange(this, BR.outputCheckAmount)
mCallbacks.notifyChange(this, BR.outputTipAmount)
mCallbacks.notifyChange(this, BR.outputTotalDollarAmount)

The size of your app doesn’t matter too much when considering this, but if you have a View in your application that is referencing many properties and a use case where only one or two of the properties change. Then, notifying the View of individual property changes, as you suggested, can be more efficient.

However, for our app, it would actually be less efficient to notify the View of individual property changes, because we’re almost always changing at least 3 properties and on save/load, 5. If we were to notify the View of each property change individually, we would spam the View each time with 5 notifications to process instead of one with BR._all.

Would be nice to see another video showing how to replace this using LiveData (via ViewDataBinding.setLifecycleOwner).

Thank you for this feedback and for watching. This is something we might be able to include in a Data Binding or LiveData course, more focused on different ways to integrate these two concepts. In the meantime, there is a great blog post by Paulina Szklarska that walks through an example of using this approach.

Really nice. Voice is clear and they way you go about introducing concepts is super easy to follow. Loved every bit of your videos..

Thank you! I'm really happy to hear that! :-)

Very well explained! Congratulations! Just got a bit lost when u change the name of variables and classes, and don't show doing it on video, but is not a very big deal. I recommend!

Hello Eric.
I am having trouble while importing the BR.
Can you detect something wrong in my project?
Below some Print Screen about it:

I am having the same issue with the BR import, did you manage to resolve your issue?

Not yet.

Hi There,

Thank you for providing screenshots Rodrigo! I tried to reproduce this using the sample project and changing the dependency versions to match yours, but I wasn't able to reproduce :-( .

Since the BR file is generated based on your bindings, it can sometimes be the case that a clean and rebuild through Android Studio resolves issues like these. You can try this by accessing the Android Studio menu (Build -> Clean Project), then (Build -> Rebuild Project).

Alternatively, if you can push your project to a public Github repo, I would be happy to pull it down and help you troubleshoot.



Lessons in Android Model-View-ViewModel Pattern