Kotlin LiveData Guide☁️

Betül Necanlı
7 min readFeb 3, 2023

--

LiveData is an observable data holder class from the Android Architecture Components library that can be observed within a given lifecycle. It is designed to be used as a holder for data that is meant to be exposed to the UI.

There are several advantages of using LiveData:

  1. Lifecycle Awareness: LiveData is aware of the lifecycle state of its observer and only delivers updates when the observer is in an active state. This helps to prevent crashes due to stopped activities, and reduces the need for manual lifecycle management in the observer.
  2. Safe Observer Configuration: LiveData automatically removes observers when they are no longer in use, preventing memory leaks and reducing the need for manual cleanup.
  3. No manual updates: LiveData provides automatic updates to its observer, reducing the need for manual updates and the potential for bugs that can be introduced through manual updates.
  4. Background thread support: LiveData can be updated on a background thread and will automatically post updates to the main thread, ensuring that updates are delivered on the correct thread and avoiding the need for manual thread management.
  5. Easy integration with other architecture components: LiveData integrates well with other Android architecture components such as viewModel and Room, making it easier to manage data in your app.
  6. Immutable data: LiveData is designed to hold immutable data, which makes it easier to reason about and reduces the risk of bugs.
  7. Simplified Testing: Because LiveData separates the concerns of data and presentation, it can make it easier to write tests that focus on the data being displayed in the UI, rather than the details of how it is displayed.

Here’s an example of how you can use LiveData in a simple use case:

And in your activity or fragment:

In this example, MainViewModel has a private mutable LiveData object, _number, which is exposed as a public LiveData object, number. The observe method is used to observe the data and update the TextView whenever the data changes.

LiveData vs MutableLiveData

LiveData and MutableLiveData are both classes from the Android Architecture Components library.

LiveData is an observable data holder class that can be observed within a given lifecycle. It is designed to be used as a holder for data that is meant to be exposed to the UI. The data is observable, meaning that it can be observed by other components, such as an Activity or Fragment, for changes. However, the data itself cannot be modified.

Here’s an example:

In this example, MainViewModel has a public LiveData object, number, which cannot be modified directly.

MutableLiveData, on the other hand, is a subclass of LiveData that can be modified. It is a mutable data holder that can be observed within a given lifecycle.

Here’s an example:

In this example, MainViewModel has a private mutable LiveData object, _number, which is exposed as a public LiveData object, number. The increment method is used to modify the data by incrementing its value.

In general, it is recommended to use LiveData for data that is meant to be exposed to the UI, and to use MutableLiveData for data that is meant to be modified. This helps to enforce a separation of concerns and promotes a clean architecture.

Activity vs Fragment

The difference between using LiveData in an Activity versus a Fragment lies in the lifecycle management.

An Activity has a longer lifecycle than a Fragment, as it represents a single, focused thing that a user can do. When an Activity is stopped or destroyed, all of its Fragments are also stopped or destroyed, but the Activity can be restored to its previous state when the user navigates back to it.

In contrast, a Fragment has a shorter lifecycle that is directly tied to its parent Activity. When the Activity is stopped, its Fragments are also stopped, and when the Activity is destroyed, its Fragments are also destroyed. When the Activity is restarted, its Fragments are also restarted.

When using LiveData in an Activity , it is recommended to observe the data in the Activity's onCreate method and remove the observer in the Activity's onDestroy method, so that the observer is active only when the Activity is in the foreground.

Here’s an example:

When using LiveData in a Fragment, it is recommended to observe the data in the Fragment's onCreateView method and remove the observer in the Fragment’s onDestroyView method, so that the observer is active only when the Fragment is in the foreground.

Here’s an example:

In both cases, the LiveData object is observed during the active lifecycle of the Activity or Fragment, and the observer is removed when the Activity or Fragment is no longer in the foreground, to avoid any unnecessary updates. This ensures that the observer only receives updates when it is actively observing the data, and helps to prevent memory leaks.

MediatorLiveData

MediatorLiveData is a subclass of LiveData that allows you to observe multiple LiveData objects and combine their values into a single LiveData object. It acts as a “mediator” between multiple LiveData sources and provides a single source of truth for your UI. Here are a few examples of how to use MediatorLiveData:

  1. Merging LiveData sources: Suppose you have two LiveData sources, liveData1 and liveData2, and you want to observe the combined value of both sources in your UI. You can use a MediatorLiveData to observe both sources and merge their values:

In this example, the MinViewModel class has two private mutable LiveData members liveData1 and liveData2, and a public mediator LiveData member mergedData. The mergedData LiveData is constructed by observing both liveData1 and liveData2 and merging their values into a single Pair object. The mergedData LiveData can be observed by UI components and any changes to liveData1 or liveData2 will be automatically reflected in the UI.

Another example:

This code defines a MainViewModel class that extends ViewModel. The MainViewModel class has two private MutableLiveData members _word1 and _word2 and a private MediatorLiveData member _result.

The init block initializes the MediatorLiveData _result by adding _word1 and _word2 as sources. The _result LiveData is updated whenever either _word1 or _word2 changes by concatenating their values and setting the result as the value of _result.

The MainViewModel also has two functions, setWord1 and setWord2, for setting the value of _word1 and _word2 respectively. Additionally, there is a public result property that exposes the _result LiveData.

2. Filtering LiveData sources: Suppose you have a LiveData source, liveData, and you only want to observe its value if it meets a certain condition. You can use a MediatorLiveData to observe the source and filter its values:

In this example, the MainViewModel class has a private mutable LiveData member liveData and a public mediator LiveData member filteredData. The filteredData LiveData is constructed by observing the liveData and filtering its values to only include values greater than zero. The filteredData LiveData can be observed by UI components and any changes to liveData will be automatically reflected in the UI if they meet the condition.

The basic idea is to use a MediatorLiveData to observe one or more LiveData sources, and use its addSource method to merge or filter the values of those sources.

Transformations

Transformations are a set of utility functions that allow you to transform the data in a LiveData object. You can use these functions to perform operations such as mapping, filtering, and aggregating the data in a LiveData object. Here’s an example:

In this example, the MainViewModel class has a private mutable LiveData member _word and a public read-only LiveData member . The length LiveData is constructed by using the Transformations.map function to map the value of the _word LiveData to its length. The length LiveData can be observed by UI components, and any changes to the _word LiveData will be automatically reflected in the UI as the length of the word.

  1. Map Transformations

You can use the map transformation to perform a calculation on the data stored in a LiveData object. This can be useful, for example, if you need to convert a raw data value into a more usable form.

In this example, the _temperature LiveData is storing the temperature in Kelvin. The temperatureCelcius LiveData is created by transforming the data stored in _temperature using the map function. The map function takes the raw temperature value in Kelvin and converts it to Celsius.

In this example, the _firstName and _lastName LiveData objects are storing the first and last name of a user. The fullName LiveData is created by using a MediatorLiveData and map transformation. The MediatorLiveData is used to observe both _firstName and _lastName. The map function takes the values stored in _firstName and _lastName and combines them into a single string, which is then set as the value of fullName.

2. SwitchMap Transformations

You can use the switchMap transformation to dynamically change the source LiveData that is being observed. This can be useful when the source LiveData is dependent on the value of another LiveData.

In this example, the _selectedUserId LiveData is storing the ID of the selected user. The selectedUser LiveData is created by transforming the data stored in _selectedUserId using the switchMap function. The switchMap function takes the selected user ID and passes it to the userRepository.getUser function, which returns a LiveData object containing the selected user.

3. Zip Transformations

You can use the zip transformation to combine multiple LiveData objects into a single LiveData object that is updated whenever any of the source LiveData objects change.

In this example, liveData1 and liveData2 are two LiveData instances, and result is a MediatorLiveData that combines the values from liveData1 and liveData2 into pairs. The addSource method is used to add the two LiveData instances as sources for result . The zip transformation is implemented by combining the values from liveData1 and liveData2 into pairs, and updating the value of result accordingly.

--

--

Betül Necanlı
Betül Necanlı

Written by Betül Necanlı

Kotlin, Android Programming, Data Structures&Algorithms, Math. https://www.youtube.com/@betulnecanli

No responses yet