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.

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.