LiveData Clean Code using MVVM and Android Architecture Components
This article will talk about things related to every android developer heart : handling lifecycle events, making code cleaner. Remember those days where activity used to hundred or thousand of lines, you most probably have faced this challenges. Perhaps this would be no surprise we’ve all looked at the mess and felt the relief of our messy activity work.
For those familiar with Uncle Bob, Clean Architecture is no stranger. It’s a set of architectural guidelines based on SOLID principles.
This principles helps to achieve: testability, separation of concerns, modularity, among some other good practices.
In our simple priority order, developer wants there business as well as UI logic should be unit tested.
Views aren’t easy to test, as it contains android related frameworks.
Views should be dumb and implement Passive View pattern. Passive View pattern is a concept that UI logic should be minimum. Coming to Single responsibility principle view have responsibility of rendering. It should display the view state currently the user is.
For example: If we are implementing login screen, you should not write validation logic inside UI, it should be responsibility of the presentation layer.
As View has context its a gateway for android related frameworks too.We should try not to put conditional logic inside our view, as views does not support unit testing. We can test UI using Roboelectric , Instrumentation testing or Espresso.
The Presentation layer should be responsible of having business logic (like input validation), communication to data layer and provide data for UI rendering. Presentation layer (ViewModel or Presenter) does not use android framework it should be agnostic, so you can encapsulate all logic and that makes it testable. We can use JVM unit testing here. It should not have any direct dependency of view.
In this article I’m following MVVM architectural pattern. The main pillars of this pattern are :
- The View — that informs the ViewModel about the user’s actions
- The ViewModel- presentation layer, having UI logic and taking to data model.
- The DataModel — abstracts the data source. It can have both remote and local data. The ViewModel works with the DataModel to get and save the data.
View Model should not have any reference of view.
We have issue like what should be scope of view model on configuration change, will it die if activity dies. Suppose we have long running operations in background and if configurations changes and viewmodel has reference to view it might lead to memory leaks and null pointer exceptions.
Recently during Google I/O, Google introduced Livedata and ViewModel as part of architecture components. Android Architecture Components is a new collection of libraries to help developers manage lifecycle issues, write modular code and provide great user experience.
We can take advantage of lifecycle components. Our view activity or fragment have a lifecycle owner. Using this lifecycle components we can make our presentation layer aware of this. Our ViewModel can now outlive configuration changes (such as rotation) and survive as it is persisted. If view is resuming after configuration change it would get back the previous ViewModel, this can help to achieve view state consistency.
ViewModel
class allows data to survive configuration changes such as screen rotationsThere is also AndroidViewModel which comes with context, but be aware of it using as it cannot be unit tested and polluting with android frameworks. It’s somewhat defeat the purpose of separation of concern.
LiveData is a observer wrapper over a model data that is lifecycle aware
ViewModel exposes livedata that view observes it. Whenever livedata changes view gets notified and it could re-render itself.
You can use either setValue() (used in ui thread) or postValue() (used in background thread) when you want to emit item from livedata.
LiveData knows about your activity state because it has lifecycle owner which we pass in observe parameter, so livedata will not trigger change method if view is destroyed.
- Activity i.e. View is observing to live data exposed by ViewModel.
- Since livedata is present inside View Model it’s retains on configuration change.
- ViewModel does not have activity reference so there would be no memory leaks, no need to handle lifecycle events such as we do with RxJava unsubscribe observable either on onStop or onDestroy.
Using LiveData with Domain Layer or Repository Layer
Following with separation of concern principle we should have a separate layer for data i.e. repository, its a single source of contact for getting data. You can have multiple repositories depends on use cases.
Repository can have instances of :
- Remote i.e. network
- Database
- Cache or Shared Preferences.
Flow of information is unidirectional.
You could also have a interactor present which could have some core logic and that exposes livedata to viewmodel which makes interactor also lifecycle aware.
The benefit using livedata in repository is that your repository layer also becomes lifecycle aware.
Following MVVM principle viewmodel should have business logic. Suppose we make an api call if it fails viewmodel should ideally have logic for error displays. For observing livedata we need life cycle owner. ViewModel doesn’t have lifecycle owner, to observe livedata inside viewmodel we can bind that bind that livedata using databinding. As livedata supports data binding we can leverage that.
binding.setLifecycleOwner(this)
The LifecycleOwner that should be used for observing changes of
LiveData in this binding
Livedata is also helpful for navigation, you don’t need to create interface (called as navigator) that needs to be implemented by view and injected in viewmodel.
ViewModel would expose livedata suppose some itemClick livedata and view would observe to it. This way you can expose livedata events that viewmodel wants view to handle it.
For communication between viewmodel and repository we use Transformations. You use transformation methods to carry information across the observer’s lifecycle.
Transformations has map and switchMap operators similar to RxJava which can converts on livedata to different one.
You can also use Mediator livedata to create your own custom operators. MediatorLiveData is used for observing multiple livedata sources and perform on there changes.
If using retrofit I’ve created a wrapper above it where you can achieve onSubscribe, onSuccess and onError if coming from RxJava background. I will add sample github repository link at the end.
Suppose activity needs to tell viewmodel to load data from api. In below snippet we expose loadData livedata activity calls that livedata and viewmodel is listening to the change and calling the api and the response objected is binded to view.
You can write a binding adapter for updating the view
Even for folks using RxJava, can use RxJava in repository layer and expose livedata to viewmodel, which will benefit as its lifecycle aware.
Creates an Observable livedata stream from a ReactiveStreams publisher. SO you could use it to convert RxJava observables to livedata.
RxJava has very rich operators and threading. It also comes with some steep learning curve. RxJava and livedata both compliment each other, use RxJava in data layer where you can use complex operators and return livedata to viewmodel.
You can extend livedata for different use-cases. For example you can create LocationLiveData as singleton in your application and only emit values when some subscriber is active.
When extending live data we need to implement some methods.
- The
onActive()
method is called when theLiveData
object has an active observer. - The
onInactive()
method is called when theLiveData
object doesn't have any active observers. - The
setValue(T)
method updates the value of theLiveData
instance and notifies any active observers about the change
There is lot that can be done using livedata as its lifecycle aware.
In the end I would like to conclude that we got a cleaner code and separation of concerns for each layer using livedata. I have added sample app link for reference below.
Thanks for reading and if you like the article, remember to clap.