LiveData. Make sure you read that post before this one, as this is a continuation.
If you can recall from the previous post, the diagram below was given as an indication of how we will be structuring our “Date Countdown” app. 
In this post, we will create the EventListViewModel and AddEventViewModel shown in the diagram above. You can find all the source code for this post here. Before we dive into creating the ViewModels, we should first take a look at what a ViewModel is.
What is a ViewModel?
A ViewModel is not a new concept, nor is it neccesarily an Android concept. The name ViewModel came from the MVVM pattern designed by Microsoft in around 2005. With the new Architecture Components, one of the new classes is the ViewModel class.
ViewModels are responsible for preparing data for the View. They expose data to any view that is listening for changes. The ViewModel class in Android has some specific facts you should keep in mind when using it:
- A
ViewModelcan retain its state across Activity configuration changes. The data it holds is immediately available to the next Activity instance without needing to save data inonSaveInstanceState()and restore it manually. - A
ViewModeloutlives the specific Activity or Fragment instances. - A
ViewModelallows easy sharing of data across Fragments (meaning you no longer need to coordinate actions via an activity). - A
ViewModelwill stay in memory until theLifecycleit’s scoped to goes away permanently – in the case of an Activity, once it finishes; in the case of a Fragment, once it’s detached. - Since a
ViewModeloutlives the Activity or Fragment instances, it shouldn’t reference any Views directly inside of it or hold reference to a context. This can cause memory leaks. - If the
ViewModelneeds an Application context (e.g. to find a system service), it can extend theAndroidViewModelclass and have a constructor that receives the Application in the constructor.
Creating ViewModels for the Date Countdown App
EventListViewModel
The EventListViewModel class will be used for the list of events that we view when the date countdown app is opened up for the very first time.
- Create a class called
EventListViewModel.Make sure it extendsViewModelfrom the Architecture components class. In this class, we are going to migrate the code that was previously placed in theEventListFragmentinto theViewModel. - Add the
LiveDatavariable to theEventListViewModel. The EventRepository variable will be injected using Dagger. TheEventListViewModelclass should look like this now:public class EventListViewModel extends ViewModel implements CountdownComponent.Injectable { private LiveData<List<Event>> events = new MutableLiveData<>(); @Inject EventRepository eventRepository; @Override public void inject(CountdownComponent countdownComponent) { countdownComponent.inject(this); events = eventRepository.getEvents(); } public LiveData<List<Event>> getEvents() { return events; } } - Now in the
EventListFragment, we will want to replace the event loading code we wrote in the previous post by rather getting the list of events through theViewModel.public class EventListFragment extends Fragment { private EventListViewModel eventListViewModel; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { //.. inflate view etc eventListViewModel = ViewModelProviders.of(this, new CountdownFactory(countdownApplication).get(EventListViewModel.class); eventListViewModel.getEvents().observe(this, events -> { adapter.setItems(events); }); return v; } }The line that contains the
ViewModelProvider.of(..)will either create a newEventListViewModelclass if one doesn’t exist, or it will fetch the one that exists for the scope that is defined (in this case it is theEventListFragment). This is where the magic happens. When the device is rotated and the fragment gets recreated, theViewModelthat will be returned here will be the one that was previously used. This allows us to retain the state of the screen without having to manually save information to theBundleand restore it ourselves.The
Eventdata is now obtained from theEventListViewModel. As mentioned previously, by passingthisas the first parameter, theLiveDataobservable will be managed automatically for you. This means the fragment will take care of disposing the observables when the fragment is no longer used. The observable callback won’t be called if the fragment is not started or resumed. - Create a Dagger
CountdownComponent, we will use this to inject the dependencies into theViewModel.@Singleton @Component(modules = {CountdownModule.class}) public interface CountdownComponent { void inject(EventListViewModel eventListViewModel); void inject(AddEventViewModel addEventViewModel); interface Injectable { void inject(CountdownComponent countdownComponent); } } - Create a custom
ViewModelProvider.NewInstanceFactorythat will be used to instantiate theViewModel. When theViewModelProviderneeds to create a new instance of aViewModelit will use thecreatemethod defined in the factory below.public class CountdownFactory extends ViewModelProvider.NewInstanceFactory { private CountdownApplication application; public CountdownFactory(CountdownApplication application) { this.application = application; } @Override public <T extends ViewModel> T create(Class<T> modelClass) { T t = super.create(modelClass); if (t instanceof CountdownComponent.Injectable) { ((CountdownComponent.Injectable) t).inject(application.getCountDownComponent()); } return t; } } - To delete an
Event, we can add a method to theViewModel.This will delegate to theEventRepositoryto delete the event. In this example, we are using RxJava to delegate to a background thread.public class EventListViewModel extends ViewModel implements CountdownComponent.Injectable { //.. public void deleteEvent(Event event) { eventRepository.deleteEvent(event) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new CompletableObserver() { @Override public void onSubscribe(Disposable d) { } @Override public void onComplete() { Timber.d("onComplete - deleted event"); } @Override public void onError(Throwable e) { Timber.e("OnError - deleted event: ", e); } }); } }
We will now use this delete method in the Fragment where we delegate to the ViewModel:
View.OnClickListener deleteClickListener = v -> {
Event event = (Event) v.getTag();
eventListViewModel.deleteEvent(event);
};
For the AddEventViewModel implementation, take a look at the full source code example here. We now have a DateCountdown app that loads events from the database using Room and the ability to add new events. We’ve used ViewModels to ensure that the data is retained on rotation and to separate our code out from our fragment.
In Summary
The new Android Architecture Components address common scenarios that we haven’t been able to handle easily before. Handling screen rotation is now really easy by using a ViewModel and the ViewModelProvider.
Be sure to checkout the full code sample available here to see everything in action and the relevant unit tests. Let me know what your thoughts are on twitter @riggaroo.
References
- ViewModel Documentation
- Android Architecture Components Documentation
- Date Countdown App – Full Code Sample
- Dagger 2 Testing Sample – Chiu-ki Chan
Thanks to Erik Hellman and Yigit Boyar for proof-reading this post.
