Internals of Android Architecture Components Part II— LiveData
This post series explores the implementation of Android Architecture Components (AAC) so we make the magic go away. In this instalment we’ll focus on LiveData
. You can see the other posts here:
What is LiveData?
Let’s look at the definition from Android’s documentation:
L
iveData
is an observable data holder class. Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. This awareness ensures LiveData only updates app component observers that are in an active lifecycle state.
In summary:
LiveData
follows the Observer pattern, notifying subscribers of new data being available.LiveData
isLifecycle
aware, which means that it will notify subscribers only when it’s in an active lifecycle state, and it will dispose the subscription when theLifecycle
terminates and is destroyed.
How does LiveData implement the Observer pattern?
Now that we have a brief introduction to the concept, let’s look at how it implements the Observer pattern.
The following snippet is how LiveData is expected to be consumed, as specified by Android’s documentation:
First, the ViewModel
creates a MutableLiveData
and exposes it. Then, the Activity
will consume it by creating an Observer
, and calling .observe(this, nameObserver)
.
Diving into observe
, we can see how it manages the subscriptions:
The first thing we notice is that observe
depends on a LifecycleOwner
and the Observer
instance we create. When the lifecycle is not in the DESTROYED
state, observe
creates a LifecycleBoundObserver
instance using the LifecycleOwner
and the Observer
.
After ensuring we don’t add twice the same Observer
with different LifecycleOwner
s, it sets the wrapper to listen for Lifecycle
events.
Now that we know how subscription works, let’s look at how the notification of observers is executed:
Setting the value is rather straight forward. LiveData
will assert we are on the main thread, update mData
to hold the latest data, and notify the observers.
In dispatchingValue
we can see that it will invalidate a previous dispatch if needed, and iterate the observers using considerNotify
where:
- It checks that the lifecycle is active.
- It checks that the version dispatched is newer than the last version received.
- Calls
onChanged
with the data we are setting.
We’ll revisit later how considerNotify
won’t post the event if the Lifecycle
is not active, and as a note; postValue
is able to dispatch new data from any thread in a safe manner, but this is outside of the scope of this article.
How doesLiveData become Lifecycle aware?
As we disovered from the first post of this series, the ViewModel
will be reused while the Activity
is destroyed and recreated, and LiveData
will continue to work undisturbed by events such as screen rotations and backgrounding the app.
When we looked at how the LiveData
is observed, we noticed that a LifecycleBoundObserver
is created by wrapping the real observer and the LifecycleOwner
.
Disposing the subscription
When the Activity
or Fragment
is destroyed, the subscription will have to be automatically disposed. Let’s look into how it’s achieved by looking at the LifecycleBoundObserver
.
In the implementation we can see:
shouldBeActive
actively checks the Lifecycle state of the owneronStateChanged
will removethis
from the observer list when theDESTROYED
state is reached. This allows to create the observer inonCreate
without having to worry about unsubscribing or disposing of it in any other way inonDestroy
.
Finally, how does Activity
implement LifecycleOwner
and when is it tagged as DESTROYED
?
The first thing we can see is that SupportActivity
uses a LifecycleRegistry
as the instance of Lifecycle
, and that this
is passed into the constructor. Our guess is that LifecycleRegistry
will be listening to Activity
state changes in it, however it seems to be doing so in a somewhat obscure way. We’ll resort to using a breakpoint in onStateChanged
to inspect the stack trace of callers:
Browsing the stack trace, we can see that the events are sent from a ReportFragment
instance.
The ReportFragment
is injected in the Activity
in onCreate
, and behaves as a headless Fragment
:
Delaying notifications on non-active Lifecycle state
Remember how considerNotify
didn’t post the events when the Lifecycle
is inactive? Let’s look at how we recover the data when the Lifecycle
becomes active again.
The key is in LifecycleBoundObserver
where onStateChanged
calls activeStateChanged
, implemented in ObservableWrapper
, to re-submit the latest value when it becomes active again:
Summary
LiveData
proposes an Observer pattern as a solution to simplify how to pass data to components across their lifecycle.LiveData
will always dispatch events to the main thread.LiveData
builds upon theLifecycle
AAC.- Any object implementing
LifecycleOwner
can be used withLiveData
. Most of the time you’ll be usingActivity
/Fragment
/Service
which already implement it for you. LiveData
will not deliver values while theLifecycle
is inactive (e.g. paused), and will submit as soon as theLifecycle
becomes active again (e.g. resumed).LiveData
simplifiesLifecycleOwner
by automatically disposing observers when theDESTROYED
event is triggered in the lifecycle.- On
SupportActivity
(and all subclasses) Android uses yet again a headlessFragment
to split responsibilities. In this case,ReportFragment
is used to dispatch lifecycle events to observers. android.support.v4.app.Fragment
dispatches the events on theperformXYZ()
methods, where XYZ is a lifecycle event.