Bri Manning

Issues with LiveData in Android Architecture Components

December 10, 2017

On Friday, I published another blog post for Rocket Insights with some insight into how we had been working Android Architecture Components.

Here’s that article in full:

As written about previously, Rocket has been developing new Android apps using Android Architecture Components. Overall, it has been a good experience with clear separation of concerns, a prescribed, defined architecture, and some useful classes along with it.

While is has been useful, one pitfall that we ran into was in using LiveData.

Many of the examples you’ll find typically go like this:

myViewModel.myLiveData  
    .observe(this /*Activity or Fragment*/, Observer { myLiveData ->
  // do something with myLiveData
})

That’s great in many cases. What about when you need to observe LiveData outside of the lifecycle that comes with Activities or Fragments?

One might think you can do the following:

val myLiveData = MutableLiveData<SomeData?>()  
myLiveData.observeForever(object : Observer<SomeData?> {  
  override fun onChanged(someData: SomeData?) {
    // do something with someData
    myLiveData.removeObserver(this)
  }
})

However, that will occasionally lead to exceptions. We’ve seen ConcurrentModificationExceptions, ArrayIndexOutOfBoundsExceptions, NullPointerExceptions, IllegalArgumentExceptions, and IndexOutOfBoundsExceptions in our development. Exceptions can happen when adding the observer or removing it. Even if you catch these, eventually observers will stop working entirely and while your app may look like it’s responding, it becomes inoperable. You could use observeForever by itself, but that isn’t the intended use-case here.

Similarly, you might think you can do this:

myViewModel.myLiveData  
    .observe(this /*MyActivity*/, Observer { myLiveData ->
  // do something with myLiveData
  myViewModel.myLiveData.removeObserver(this@MyActivity)
})

However, eventually you’ll still run into those exceptions.

As a result, we’ve ended up passing our lifecycle references in situations where we need to observe changes happening outside of the actual lifecycle object. That is clearly less than ideal. Since one of the advantages of using Room is that it returns LiveData, we’re somewhat backed into the corner on that.

Going forward, we’ll have to take a hard look at just how we use Android Architecture Components. A mix of using ViewModels and LiveData in Activities and Fragments while using RxJava for interacting between ViewModels and Repositories or other data layers seems like it could be the optimal solution, even if it means you’re using different frameworks and paradigms within the same app.

Software development is a learning process defined by adapting to change. Clearly, Android development is no different.

I’ve really liked working with Android Architecture Components in most cases. I think that it gives you a lot of advantages. Unfortunately, there are still some growing pains even after the official release.