



Introduction

In the last part, I wrote about Model-View-ViewModel (MVVM) and its structure, together with an implementation of the same sample movie search app I introduced in the first part of the series. As mentioned in part 4, MVVM is chosen by Google as the standard for writing android apps owing to its advantages over other architectural patterns. The architecture components introduced in Google I/O 2017 and now as part of Jetpack libraries in Google I/O 2018 help with better and cleaner development of this architecture in android, taking into account OS characteristics such as activity/fragment lifecycles, saving state, etc.

So from this part on, I am going to focus on MVVM and implement the same movie search app using architecture components.

The screenshot of the app is shown below. The user enters part of a movie name and the app looks it up on the network and returns the list containing the search term.

To proceed with the architecture components, it is best to discuss them individually and explain how they fit (replace) the respective part in the MVVM structure outlined in part 4. This post is about LiveData as the architecture component for implementing the observer pattern, an alternative to RxJava.

I will not deal with ViewModel architecture component in this post. Detailed discussion on ViewModel is the subject of the next part, which shows its combined application with LiveData .

The full source code corresponding to this post is in the ‘mvvm-livedata’ branch of my Github repo here.

Observer pattern in MVVM

Let’s first review the observer pattern in MVVM, with reference to the movie search app:

It has three components:

Defining the variable to be observed: the variable to be observed is inside the View Model. In the sample app it is the list of movie search results. Building the observable: the observable (in the View Model) is built on top of the variable to be observed. This enables broadcasting the changes in the variable to any observer listening to it. For example, here, when the response containing the movie search results returns from the server, the observable broadcasts the new results. Observing the observable: the observer (in the View) listens to the changes broadcast by the observable and does the visual action needed, such as displaying the list upon receiving it. The observer can observe the changes in the observable by subscribing to it.

How RxJava fits within the above

The search result to be displayed in the RecyclerView is a list of string objects. so the variable to be observed is List < String > . The variable is inside the View Model in MainViewModel class. The observable is also inside the View Model in MainViewModel class: resultListObservable : PublishSubject < List < String >> . The PublishSubject is a type of observable in RxJava whose observer only receives the broadcasts from when it has already subscribed to it:

constructor(mMainModel: MainModel) : this() { mainModel = mMainModel resultListObservable = PublishSubject.create() resultListErrorObservable = PublishSubject.create() itemObservable = PublishSubject.create() } 1 2 3 4 5 6 constructor ( mMainModel : MainModel ) : this ( ) { mainModel = mMainModel resultListObservable = PublishSubject . create ( ) resultListErrorObservable = PublishSubject . create ( ) itemObservable = PublishSubject . create ( ) }

When the user clicks the ‘FIND’ button, the following method from the View Model is called in which the resultListObservable broadcasts a new result every time the response is successful:

fun findAddress(address: String) { val disposable: Disposable = mainModel.fetchAddress(address)!!.subscribeOn(schedulersWrapper.io()).observeOn(schedulersWrapper.main()).subscribeWith(object : DisposableSingleObserver<List<MainModel.ResultEntity>?>() { override fun onSuccess(t: List<MainModel.ResultEntity>) { entityList = t resultListObservable.onNext(fetchItemTextFrom(t)) } override fun onError(e: Throwable) { resultListErrorObservable.onNext(e as HttpException) } }) compositeDisposable.add(disposable) } 1 2 3 4 5 6 7 8 9 10 11 12 13 fun findAddress ( address : String ) { val disposable : Disposable = mainModel . fetchAddress ( address ) ! ! . subscribeOn ( schedulersWrapper . io ( ) ) . observeOn ( schedulersWrapper . main ( ) ) . subscribeWith ( object : DisposableSingleObserver < List < MainModel . ResultEntity > ? > ( ) { override fun onSuccess ( t : List < MainModel . ResultEntity > ) { entityList = t resultListObservable . onNext ( fetchItemTextFrom ( t ) ) } override fun onError ( e : Throwable ) { resultListErrorObservable . onNext ( e as HttpException ) } } ) compositeDisposable . add ( disposable ) } The observer in the View ( MainActivity class) then subscribes to this observable by:

mMainViewModel.resultListObservable.subscribe(Consumer { hideProgressBar() updateMovieList(it) }) 1 2 3 4 mMainViewModel . resultListObservable . subscribe ( Consumer { hideProgressBar ( ) updateMovieList ( it ) } )

which hides the progress bar as well as updates the search results by updating the list in the adapter for the recyclerView.

Similar observer is also defined in the View for the unsuccessful responses from the server.

For details of the code please refer to part 4 of this series or the ‘mvvm-rxjava’ branch of my Github repo.

How LiveData fits within the above

LiveData is another tool for implementing the observer pattern. So similar steps as in RxJava can be carried out herein:

The first step is exactly the same. The observable is the list of movie search results which is inside a list of string objects List < String > inside the MainViewModel class. The observable is built using LiveData < List < String >> or MutableLiveData < List < String >> . There is a difference between the two as the name suggests: you cannot broadcast any values with LiveData < List < String >> because it is non-mutable. So for broadcasting the changes to the variable in the View Model ( MainViewModel ), MutableLiveData < List < String >> has to be used. The LiveData < List < String >> is used only for observing the observable in the View ( MainActivity ), which is not allowed to broadcast any changes. This is because the View is always passive and should only react to changes. For broadcasting the change, either postValue or setValue methods can be used for LiveData . The former is used for the main thread and the latter for the background thread. Here’s how LiveData broadcasts the result every time a successful result is returned from the server in our benchmark movie app:

fun findAddress(address: String) { mainModel.fetchAddress(address)!!.subscribeOn(schedulersWrapper.io()).observeOn(schedulersWrapper.main()).subscribeWith(object : DisposableSingleObserver<List<MainModel.ResultEntity>?>() { override fun onSuccess(t: List<MainModel.ResultEntity>) { entityList = t resultListObservable.postValue(fetchItemTextFrom(t)) } override fun onError(e: Throwable) { resultListErrorObservable.postValue(e as HttpException) } }) } 1 2 3 4 5 6 7 8 9 10 11 12 fun findAddress ( address : String ) { mainModel . fetchAddress ( address ) ! ! . subscribeOn ( schedulersWrapper . io ( ) ) . observeOn ( schedulersWrapper . main ( ) ) . subscribeWith ( object : DisposableSingleObserver < List < MainModel . ResultEntity > ? > ( ) { override fun onSuccess ( t : List < MainModel . ResultEntity > ) { entityList = t resultListObservable . postValue ( fetchItemTextFrom ( t ) ) } override fun onError ( e : Throwable ) { resultListErrorObservable . postValue ( e as HttpException ) } } ) } Observing the observable in LiveData is also straightforward. Unlike RxJava where the observable’s dependence on the lifecycle of the activity/fragment has to be manually taken into account (by disposing the observable in onDestroy ( ) method of the Activity or onDestroyView ( ) of the Fragment , for example), it is already taken care of in LiveData simply by entering the instance of the Activity or Fragment the observer pertains to, which is the owner of the lifecycle of the View, or LifecyclerOwner . So the two introduced notions in architecture components with regards to lifecycle are: Lifecycle : an object containing info about the lifecycle of the Activity / Fragment LifecycleOwner : The Activity / Fragment the lifecycle relates to. Back to our sample app, here is how the observable for the list of search results is defined in MainActivity :

mMainViewModel.getResultListObservable().observe(this, Observer { hideProgressBar() updateMovieList(it!!) }) 1 2 3 4 mMainViewModel . getResultListObservable ( ) . observe ( this , Observer { hideProgressBar ( ) updateMovieList ( it ! ! ) } )

where this refers to MainActivity which is the LifecycleOwner .

When using LiveData , the OS automatically disposes the subscription in onDestroy() of the observer’s activity and thus avoids memory leak and possible crash.

So why LiveData over RxJava in the architecture components?

We already saw here that LiveData is not an essentially different tool to RxJava , so why was it introduced as an architecture component when RxJava could have easily managed the lifecycle by storing all the subscriptions to observables in a CompositeDispoable object and then disposing them in onDestroy() of the Activity or onDestroyView() of the Fragment using only one line of code?

Well, yes, it could, but that would need first overriding the relevant lifecycle methods besides having the basic lifecycle knowledge. This still might not make sense for some, but the fact is that according to one of the Jetpack sessions in Google I/O 2018 many developers find lifecycle management complex. The crash errors arising from not handling lifecycle dependence might be another sign that some developers, even if knowledgable of lifecycle, forget to take care of that in every Activity / Fragment they use in their app. In large apps this could become an issue, notwithstanding the negative effect it could have on productivity.

The bottom line is that by introducing LiveData , larger number of developers are expected to adopt MVVM without even having to understand the lifecycle management, memory leak and crash. Even though, I have no doubt that LiveData is not comparable with RxJava in terms of capabilities and the power it gives to developers, reactive programming and RxJava is a hard-to-understand concept and tool for many. On the other side, I do not think LiveData is meant to be a replacement for RxJava–it simply cannot–but a very simple tool for handling a controversial widespread issue experienced by many developers.

In part 7 of the series–I KNOW THIS IS PART 5 NOT 6 :-D– I will show a simple example where using LiveData leads to unexpected result and how RxJava comes to rescue.

This finishes this part on LiveData . In the next part I will write about ViewModel as another architecture component.

Stay tuned and comment below about your thought.