Firebase, ViewModels & LiveData

Luis G. Valle
3 min readOct 22, 2017

Scenario

Our Android app needs to display a list of articles stored in a Firebase RemoteDatabase.
What are the options we have?

Simple RecyclerView displaying a list of articles fetched from Firebase RemoteDatabase

1. Plain Firebase SDK

Using just the provided Firebase SDK we can request the data in our Activity onResume , listen for a callback with our Articles and update the RecyclerView adapter.

override fun onResume() {
super.onResume()

FirebaseDatabase.getInstance()
.getReference("feed/articles")
.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
if (dataSnapshot.exists()) {
adapter.updateArticles(toArticles(snapshot))
}
}
})
}

✅ Simple, works very well for a sample in a blogpost.
❌ The Activity should be listening (or observing) data and not actively fetching it
❌ Data won’t survive configuration changes, forcing a new fetch when it happens

2. Custom LiveData

We can expose our Firebase RemoteDatabase content using a custom LiveData to hide the listener.

class FirebaseDatabaseLiveData : LiveData<List<Article>>() {
override fun onActive() {
super.onActive()

FirebaseDatabase.getInstance()
.getReference("feed/articles")
.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
if (dataSnapshot.exists()) {
value = toArticles(snapshot)
})
}
}

This will move fetching data responsibility away from the Activity and introduce a much better Observer Pattern to listen for data changes

override fun onResume() {
super.onResume()

firebaseDatabaseLiveData.observe(this, Observer { articles ->
adapter.setArticles(articles)
}
})

}

We’ve done improvements but it’s still not good enough.
Using this solution our fetched data won’t survive configuration changes.

As a rule of thumb you should never need to extend LiveData , only in rare occasions this is needed.

✅ The Activity is no longer fetching data but rather observing for data changes
❌ Data won’t survive configuration changes, forcing a new fetch when it happens

3. LiveData + ViewModel

The ViewModel class is designed to store and manage your Activity data so that it will survive configuration changes, like screen rotations.

Simple architecture to fetch & observe data stored in Firebase
class ArticleViewModel : ViewModel() {
var articles: MutableLiveData<List<Article>> = MutableLiveData()

fun getArticles(): LiveData<List<Article>> {
if (articles.value == null) {
FirebaseDatabase.getInstance()
.getReference("feed/articles")
.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
if (dataSnapshot.exists()) {
articles.postValue(toArticles(snapshot))
})
}

return articles

}

Things to notice:

  • The method getArticles() returns an immutable LiveData object instead of the internal mutable one, preventing observers from modifying its content.
  • It only fetches new data from Firebase if the content of articles is empty
override fun onResume() {
super.onResume()

viewModel.getArticles().observe(this, Observer { articles ->
adapter.setArticles(articles)
}
})

}

This looks very similar to the solution from point 2, but it has one big difference: the data is stored in a ViewModel, and data stored this way will survive configuration changes in the activity

Activity lifecycle vs ViewModel

✅ The Activity is not fetching data directly but rather observing for data changes
✅ Data will survive configuration changes, a rotation won’t cause a new data fetch

Conclusion

Use ViewModel to store all the data that your Activity needs and LiveData to handle communication between Activity and ViewModel

Read More

To know more about ViewModel, LiveData and common Patterns (and anti-patterns) definitely check https://medium.com/google-developers/viewmodels-and-livedata-patterns-antipatterns-21efaef74a54 by Jose Alcérreca

This is a very simple example to illustrate how we can use Architecture Components with Firebase to produce a responsive UI, safe from memory leaks and with a good separation of responsibilities.

In a real world scenario you’ll probably need to decouple this a little bit more. For instance, extracting data fetch from the ViewModel into it’s own class will probably be a good idea.

--

--