LiveData
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.
LiveData considers an observer, which is represented by the Observer
class, to be in an active state if its lifecycle is in the STARTED
or RESUMED
state. LiveData only notifies active observers about updates. Inactive observers registered to watch LiveData
objects aren’t notified about changes.
You can register an observer paired with an object that implements the LifecycleOwner
interface. This relationship allows the observer to be removed when the state of the corresponding Lifecycle
object changes to DESTROYED
. This is especially useful for activities and fragments because they can safely observe LiveData
objects and not worry about leaks—activities and fragments are instantly unsubscribed when their lifecycles are destroyed.
The advantages of using LiveData
Ensures your UI matches your data state
LiveData follows the observer pattern. LiveData notifies Observer
objects when underlying data changes. You can consolidate your code to update the UI in these Observer
objects. That way, you don’t need to update the UI every time the app data changes because the observer does it for you.
No memory leaks
Observers are bound to Lifecycle
objects and clean up after themselves when their associated lifecycle is destroyed.
No crashes due to stopped activities
If the observer’s lifecycle is inactive, such as in the case of an activity in the back stack, then it doesn’t receive any LiveData events.
No more manual lifecycle handling
UI components just observe relevant data and don’t stop or resume observation. LiveData automatically manages all of this since it’s aware of the relevant lifecycle status changes while observing.
Always up to date data
If a lifecycle becomes inactive, it receives the latest data upon becoming active again. For example, an activity that was in the background receives the latest data right after it returns to the foreground.
Proper configuration changes
If an activity or fragment is recreated due to a configuration change, like device rotation, it immediately receives the latest available data.
Sharing resources
You can extend a LiveData
object using the singleton pattern to wrap system services so that they can be shared in your app. The LiveData
object connects to the system service once, and then any observer that needs the resource can just watch the LiveData
object. For more information, see Extend LiveData.
Work with LiveData objects
Follow these steps to work with LiveData
objects:
- Create an instance of
LiveData
to hold a certain type of data. This is usually done within yourViewModel
class. - Create an
Observer
object that defines theonChanged()
method, which controls what happens when theLiveData
object’s held data changes. You usually create anObserver
object in a UI controller, such as an activity or fragment. - Attach the
Observer
object to theLiveData
object using theobserve()
method. Theobserve()
method takes aLifecycleOwner
object. This subscribes theObserver
object to theLiveData
object so that it is notified of changes. You usually attach theObserver
object in a UI controller, such as an activity or fragment.
Note: You can register an observer without an associated
LifecycleOwner
object using theobserveForever(Observer)
method. In this case, the observer is considered to be always active and is therefore always notified about modifications. You can remove these observers calling theremoveObserver(Observer)
method.
When you update the value stored in the LiveData
object, it triggers all registered observers as long as the attached LifecycleOwner
is in the active state.
Create LiveData objects
LiveData is a wrapper that can be used with any data, including objects that implement Collections
, such as List
. A LiveData
object is usually stored within a ViewModel
object and is accessed via a getter method.
Generally, LiveData delivers updates only when data changes, and only to active observers. An exception to this behavior is that observers also receive an update when they change from an inactive to an active state. Furthermore, if the observer changes from inactive to active a second time, it only receives an update if the value has changed since the last time it became active.
class MyViewModel : ViewModel() {
// Create a LiveData with a String
val currentName: MutableLiveData<String> by lazy {
MutableLiveData<String>()
}
// Rest of the ViewModel...
}
MyActivity:
class MyActivity : AppCompatActivity() {
private val model: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Create the observer which updates the UI.
val nameObserver = Observer<String> { newName ->
// Update the UI, in this case, a TextView.
nameTextView.text = newName
}
// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
model.currentName.observe(this, nameObserver)
}
}
Update LiveData objects
LiveData has no publicly available methods to update the stored data. The MutableLiveData
class exposes the setValue(T)
and postValue(T)
methods publicly and you must use these if you need to edit the value stored in a LiveData
object. Usually MutableLiveData
is used in the ViewModel
and then the ViewModel
only exposes immutable LiveData
objects to the observers.
button.setOnClickListener { val anotherName = "XYZ" model.currentName.setValue(anotherName) }
Note: You must call the
setValue(T)
method to update theLiveData
object from the main thread. If the code is executed in a worker thread, you can use thepostValue(T)
method instead to update theLiveData
object.
Use LiveData with Room
The Room persistence library supports observable queries, which return LiveData
objects. Observable queries are written as part of a Database Access Object (DAO).
Room generates all the necessary code to update the LiveData
object when a database is updated. The generated code runs the query asynchronously on a background thread when needed. This pattern is useful for keeping the data displayed in a UI in sync with the data stored in a database.
It may be tempting to work LiveData
objects in your data layer class, but LiveData
is not designed to handle asynchronous streams of data.
If you need to use streams of data in other layers of your app, consider using Kotlin Flows and then converting them to LiveData
in the ViewModel
using asLiveData()
.
Extend LiveData
The fact that LiveData
objects are lifecycle-aware means that you can share them between multiple activities, fragments, and services. To keep the example simple, you can implement the LiveData
class as a singleton as follows:
class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
private val stockManager: StockManager = StockManager(symbol)
private val listener = { price: BigDecimal ->
value = price
}
override fun onActive() {
stockManager.requestPriceUpdates(listener)
}
override fun onInactive() {
stockManager.removeUpdates(listener)
}
companion object {
private lateinit var sInstance: StockLiveData
@MainThread
fun get(symbol: String): StockLiveData {
sInstance = if (::sInstance.isInitialized) sInstance else StockLiveData(symbol)
return sInstance
}
}
}
The implementation of the price listener in this example includes the following important methods:
- The
onActive()
method is called when theLiveData
object has an active observer. This means you need to start observing the stock price updates from this method. - The
onInactive()
method is called when theLiveData
object doesn’t have any active observers. Since no observers are listening, there is no reason to stay connected to theStockManager
service. - The
setValue(T)
method updates the value of theLiveData
instance and notifies any active observers about the change.
class MyFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
StockLiveData.get(symbol).observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? ->
// Update the UI.
})
}
Transform LiveData
You may want to make changes to the value stored in a LiveData
object before dispatching it to the observers, or you may need to return a different LiveData
instance based on the value of another one. The Lifecycle
package provides the Transformations
class which includes helper methods that support these scenarios.
Transformations.map()
Applies a function on the value stored in the LiveData
object, and propagates the result downstream.
val userLiveData: LiveData<User> = UserLiveData()
val userName: LiveData<String> = Transformations.map(userLiveData) {
user -> "${user.name} ${user.lastName}"
}
Transformations.switchMap()
Similar to map()
, applies a function to the value stored in the LiveData
object and unwraps and dispatches the result downstream. The function passed to switchMap()
must return a LiveData
object, as illustrated by the following example:
private fun getUser(id: String): LiveData<User> {
...
}
val userId: LiveData<String> = ...
val user = Transformations.switchMap(userId) { id -> getUser(id) }
The transformations aren’t calculated unless an observer is watching the returned LiveData
object. Because the transformations are calculated lazily, lifecycle-related behavior is implicitly passed down without requiring additional explicit calls or dependencies.
class MyViewModel(private val repository: PostalCodeRepository) : ViewModel() {
private val addressInput = MutableLiveData<String>()
val postalCode: LiveData<String> = Transformations.switchMap(addressInput) {
address -> repository.getPostCode(address) }
private fun setInput(address: String) {
addressInput.value = address
}
}
In this case, the postalCode
field is defined as a transformation of the addressInput
. As long as your app has an active observer associated with the postalCode
field, the field’s value is recalculated and retrieved whenever addressInput
changes.
References:
https://developer.android.com/topic/libraries/architecture/livedata