Android Architecture Components — ViewModel and LiveData

Soumya Kanti Kar
wwdablu
Published in
6 min readMay 8, 2018

Recently I planned to update an Android application that I wrote long back. While going through the code, I found many places wherein there were scopes of improvement. There were many portions of the code which was working, but something did not feel good.

While planning to improve the code, I thought why not try out the Android Architecture Components. I wanted to play around with it and learn on how we can integrate it with an existing application and measure the learning curve (which will be different from person to person but I hope to justify it)

First of, this discussion covers LiveData and ViewModel. Room is not in the scope of this discussion. These are just my through process and thinking on the implementation front. It may vary with other developers and I welcome them to share their ideas.

Gradle

To use the Android Architecture Component, LiveData and ViewModel we need to specify this in the gradle.

// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:1.1.1"

ViewModel

ViewModels provide the data to be displayed by the UI from the model, in a lifecycle aware manner. It can survive configuration changes. Bind it with the view XML and that reduces more of the boiler code to display data on UI. To create a ViewModel, we need to extend the ViewModel or the AndroidViewModel class.

The difference between the ViewModel and the AndroidViewModel class is that the later one provides you with an application context, which you need to provide when you create a view model of type AndroidViewModel. If there is no requirement of context information on the ViewModel being created then we should always use ViewModel.

public class SimpleViewModel extends ViewModel {
...
}

Now incase you need the AndroidViewModel

public class ViewModelWithContext extends AndroidViewModel {
public ViewModelWithContext(Application application) {
super(application);
}
}

One very important thing to keep in mind is that we should not store any reference to views on the ViewModel. Doing so will lead to memory leak and we all know how scary it is.

For devs who are yet to face fixing memory leak scenario, its like Conjuring demon nun with ability to rotate her head like exorcist and crazy like Pennywise clown. You need to tackle such a beast.

Live Data

Live data are basically observables. They are… alive! Meaning, any changes that occur to the data will trigger the attached active observers. In here we have used the MutableLiveData<T> implementation of LiveData.

MutableLiveData<T> provides us with a LiveData which is mutable in characteristic. It provides get and set/post methods for the data it holds. setValue method can be called from the main thread and postValue method can be called from the background thread.

Let us see an example as to how we can declare a LiveData:

private MutableLiveData<UserInfo> liveUserInfoData;

Using ViewModels

So, now that we have some basic concept ready. Let us see how we can use ViewModel from activity or fragment.

ViewModel vm = ViewModelProviders.of(this).get(ViewModel.class);

In this case, this refers to as the fragment or the activity. If we read, it literally means “provide me the view model of <this activity or fragment> which is of type <view model class”. Hence, the view model of this parent will always return the same instance for the same instance of the parent.

Now if the activity has an instance of a ViewModel which stores an information, then even if orientation change occurs the fragment or activity will return the same view model hence, the data still remains.

Use Case Scenario

Now let us see how we can combine both ViewModel and MutableLiveData to present an UI. The use case scenario is that we are going to simulate a web service call to fetch the user information and once retrieved we will display it in the UI.

First, let us create the ViewModel which will have the mock web service call to create an instance of the UserInfo object.

public class UserViewModel extends ViewModel {    private MutableLiveData<UserInfo> liveUserInfoData;
private int flag = 0;
public UserViewModel() {
liveUserInfoData = new MutableLiveData<>();
Observable.interval(5000, 1000, TimeUnit.MILLISECONDS)
.takeWhile(new Func1<Long, Boolean>() {
@Override
public Boolean call(Long aLong) {
return flag == 0;
}
})
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Long>() {
@Override
public void call(Long aLong) {
UserInfo userInfo = new UserInfo();
userInfo.setName("John Doe");
userInfo.setLocation("Caldeum Bazaar");
liveUserInfoData.setValue(userInfo);
flag = 1;
}
});
}
public MutableLiveData<UserInfo> getLiveUserInfoData() {
return liveUserInfoData;
}
}

The above code will create UserInfo object after the defined delay and then set the value (we have observeOn main thread). Now, as soon as the setValue is called it will notify all its observers that there is a change on the content of the live data and the respective observers need to take action.

So how do we use this ViewModel from the activity.

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinder = DataBindingUtil.setContentView(this, R.layout.a_main);
mBinder.setLifecycleOwner(this);
userViewModel = ViewModelProviders.of(this)
.get(UserViewModel.class);
mBinder.setViewModel(userViewModel);
}

We are already familiar with the call to ViewModelProviders. Using it we can get the instance of the ViewModel that we need to use. What is up with the setLifeCycleOwner call?

We want to use the LiveData for the data binding purpose and to propagate any notification to the observers about the content change, we need to call this method. This method will facilitate the bridge between the view model provide, in this case the activity and with the live data. If this call is not done, any changes in the LiveData will not be reflected on the UI.

Alright. How about the XML?

<layout>    <data>
<variable
name="viewModel"
type="com.wwdablu.soumya.androidarch.UserViewModel"/>
</data>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.liveUserInfoData.userName}"
android:id="@+id/tv_counter" />
</LinearLayout></layout>

We are binding the view model with the UI. As soon as the setValue call is done on the ViewModel, the content of the TextView will be updated.

Transformations

There is another interesting part of LiveData and that is transformations. We can transform the data before being posted on the LiveData. For example:

LiveData<String> helloUser = Transformations.map(liveUserInfoData, new Function<UserInfo, String>() {
@Override
public String apply(UserInfo input) {
return "Namaskar, " + input.getUserName();
}
});

So what would happen is as soon as the liveUserInfoData, i.e the LiveData is populated, the map method would be triggered and it would result in creating the required new output stored on LiveData helloUser.

onSaveInstanceState()

Does this mean we are done with this method and everything can be handled by the ViewModels.

Well, we will still require the onSaveInstanceState(). The reason being that ViewModels will be retained across configuration changes but not when the application is closed an relaunched. onSaveInstanceState() can survive across configuration as well as the application close scenario.

Then, why do we not use onSaveInstance() always? Well, we should be this implementation only if we need to retain information even if the application is killed. If not, then we can use ViewModel.

Conclusion

Being completely new to Android Architecture Component, picking up the basics was very fast. The learning curve is a bit low if you are familiar with observable pattern and had prior hands on with RxJava for example. Once you get the taste of it, there is no going back. It makes development smooth, and quick.

If you have any comment or feedback, then please do let me know. Also if you have another approaches of doing it, do share it with us.

--

--

Soumya Kanti Kar
wwdablu
Editor for

Android developer. Interested in working on open source Android libraries and applications.