diagram LifeData

Android Architecture Components: LiveData

We’ve already covered a lot of ground in our Android Architecture Components series. We started out talking about the idea behind the new architecture and looking at the key components presented at Google I/O. In the second post, we began our deep exploration of the main components of the package, taking a close look at the Lifecycle and LiveModel components. In this post, we’ll continue to explore the Architecture Components, this time analyzing the awesome LiveData component.

I’m assuming that you’re familiar with the concepts and components covered in the last tutorials, like Lifecycle, LifecycleOwner, and LifecycleObserver. If you’re not, take a look at the first post in this series, where I discuss the general idea behind the new Android Architecture and its components. 

We’ll continue to build on the sample application we started in the last part of this series. You can find it in the tutorial GitHub repo.

1. The LiveData Component

LiveData is a data holder. It is capable of being observed, it can hold any kind of data, and on top of that, it is also lifecycle-aware. In practical terms, LiveData can be configured to only send data updates when its observer is active. Thanks to its lifecycle awareness, when observed by a LifecycleOwner the LiveData component will send updates only when the observer’s Lifecycle is still active, and it will remove the observed relation once the observer’s Lifecycle is destroyed.

The LiveData component has many interesting characteristics:

  • prevents memory leaks when the observer is bound to a Lifecycle
  • prevents crashes due to stopped activities 
  • data is always up to date
  • handles configuration changes smoothly
  • makes it possible to share resources
  • automatically handles lifecycle

2. Observing LiveData

A LiveData component sends data updates only when its observer is “active”. When observed by a LifecycleOwner, the LiveData component considers the observer to be active only while its Lifecycle is on the states STARTED or RESUMED, otherwise it will regard the observer as inactive. During the observer’s inactive state, LiveData will stop the data update stream, until its observer becomes active once again. If the observer is destroyed, LiveData will remove its reference to the observer.

LiveData update flow

To achieve this behavior, LiveData creates a tight relation with the observer’s Lifecycle when observed by a LifecycleOwner. This capacity makes it easier to avoid memory leaks when observing LiveData. However, if the observing process is called without a LifecycleOwner, the LiveData component won’t react to Lifecycle state, and the observer’s status must be handled manually.

To observe a LiveData, call observe(LifecycleOwner, Observer) or observeForever(Observer)

  • observe(LifecycleOwner, Observer): This is the standard way to observe a LiveData. It ties the observer to a Lifecycle, changing LiveData‘s active and inactive state according to the LifecycleOwner current state.
  • observeForever(Observer): This method doesn’t use a LifecycleOwner, so the LiveData won’t be able to respond to Lifecycle events. When using this method, it is very important to call removeObserver(Observer), otherwise the observer may not be garbage collected, causing a memory leak.

3. Implementing LiveData

The generic type in the LiveData class defines the type of data that will be held. For example, LiveData holds Location data. Or LiveData holds a String

There are two main methods that must be considered in the component implementation: onActive() and onInactive(). Both methods react to the observer’s state.

Example Implementation

In our sample project we used lots of LiveData objects, but we only implemented one: LocationLiveData. The class deals with GPS Location, passing the current position only for an active observer. Notice that the class updates its value on the onLocationChanged method, passing to the current active observer the refreshed Location data.

4. The MutableLiveData Helper Class

The MutableLiveData is a helper class that extends LiveData, and exposes postValue and setValue methods. Other than that, it behaves exactly like its parent. To use it, define the type of data that it holds, like MutableLiveData to hold a String, and create a new instance.

To send updates to an observer, call postValue or setValue. The behavior of these methods is quite similar; however, setValue will directly set a new value and can only be called from the main thread, while postValue creates a new task on the main thread to set the new value and can be called from a background thread.

It is important to consider that, since the postValue method creates a new Task and posts on the main thread, it will be slower than direct calls to setValue.

5. Transformations of LiveData

Eventually, you’ll need to change a LiveData and propagate its new value to its observer. Or maybe you need to create a chain reaction between two LiveData objects, making one react to changes on another. To deal with both situations, you may use the Transformations class.

Map Transformations

Transformations.map applies a function on a LiveData instance and dispatches the result to its observers, giving you the opportunity to manipulate the data value.

Transformationsmap flow

It is very easy to implement Transformations.map. All you’ll have to do is provide a LiveData to be observed and a Function to be called when the observed LiveData changes, remembering that the Function must return the new value of the transformed LiveData.

Suppose that you have a LiveData that must call an API when a String value, like a search field, changes.

SwitchMap Transformations

The Transformations.switchMap is quite similar to Transformations.map, but it must return a LiveData object as a result. It’s a little harder to use, but it allows you to construct powerful chain reactions. 

In our project, we used Transformations.switchMap to create a reaction between LocationLiveData and ApiResponse

  1. Our Transformation.switchMap observes LocationLiveData changes.
  2. The LocationLiveData updated value is used to call the MainRepository to get the weather for the specified location.
  3. The repository calls the OpenWeatherService that produces a LiveData> as a result.
  4. Then, the returned LiveData is observed by a MediatorLiveData, which is responsible for modifying the received value and updating the weather exhibited in the view layer.

Watch out for time-consuming operations in your LiveData transformations, though. In the code above, both Transformations methods run on the main thread.

6. The MediatorLiveData

MediatorLiveData is a more advanced type of LiveData. It has capabilities very similar to those of the Transformations class: it is able to react to other LiveData objects, calling a Function when the observed data changes. However, it has many advantages when compared with Transformations, since it doesn’t need to run on the main thread and can observe multiple LiveDatas at once.

To observe a LiveData, call addSource(LiveData, Observer), making the observer react to the onChanged method from the given LiveData. To stop the observation, call removeSource(LiveData).

In our project, the data observed by the view layer that contains the weather to exhibit is a MediatorLiveData. The component observes two other LiveData objects: weatherByLocationResponse, which receives weather updates by location, and weatherByCityResponse, which receives weather updates by city name. Each time that these objects are updated, weatherByCityResponse will update the view layer with the current weather requested.

In the MainViewModel, we observe the LiveData and provide the weather object to view.

In the MainActivity, the weather is observed and its result is shown to the user.

The MediatorLiveData was also used as the basis of an object that handles calls to the OpenWeatherMap API. Take a look at this implementation; it’s more advanced than the ones above, and it’s really worth studying. If you’re interested, take a look at OpenWeatherService, paying special attention to the Mediator class.

7. Conclusion

We’re almost at the end of our exploration of Android’s Architecture Components. By now, you should understand enough to create some powerful apps. In the next post, we’ll explore Room, an ORM that wraps SQLite and can produce LiveData results. The Room component fits perfectly in this Architecture, and it is the final piece of the puzzle.

See you soon! And in the meantime, check out some of our other posts on Android app development!

  • Android SDK
    How to Use the Google Cloud Vision API in Android Apps
    Ashraff Hathibelagal
  • Java
    Android Design Patterns: The Observer Pattern
    Chike Mgbemena
  • Kotlin
    Kotlin From Scratch: Variables, Basic Types, and Arrays
    Chike Mgbemena
  • Kotlin
    Kotlin From Scratch: Nullability, Loops, and Conditions
    Chike Mgbemena
  • Android SDK
    What Are Android Instant Apps?
    Jessica Thornsby

Powered by WPeMatico

Leave a Comment

Scroll to Top