Merging Android LiveData


Merging Android LiveData

Since the adoption of reactive programming in Android by Google, a new category of life cycle aware classes were released to make the concept more usable. This paradigm shift induced a lot of changes in the android stack, and lead to the conceptualization of LiveData. This Android LiveData as we know is a data holder class on which we can put an observable to observe the changes in data it holds. This works very well with SQLite databases and API calls, but what if we have to combine both of these LiveData objects and return one single Android merged LiveData. You must be thinking, its not very straight forward. But still it is very easy to implement, in this tutorial we would have a look at how to merge two android LiveData objects and maintain a single stream of merged Android LiveData at the Activity level.

For this Android LiveData merging tutorial, it expected from you as a reader to have basic understanding of following concepts:

  • Androidx
  • MVVM Architecture
  • Android Architecture Components
  • Lifecycle Aware Programming
  • Room Database

When you need to combine LiveData objects from multiple data sources into one LiveData object, you also need to take care of a case where data is changed in one of the original LiveData objects. In such a case then the corresponding data should also be refreshed in the other LiveData which is merged with current one. In the next sections you will see how to refresh data dynamically from SQLite database when one of the API call based LiveData values are changed.

How to merge and combine multiple LiveData sources?

Once you start using LiveData in your apps you will realize that, it can solve almost every data flow problem, weather you are trying to communicate with a fragment from an activity, or you are trying to connect with a database. Even if you are trying to make an API call and return data to your activity, now-days the best practice is to use a LiveData object. But one issue that you may face by using LiveData is that, if you need data from multiple sources, for example from a web API call and a local database, then how will you combine two LiveData objects? This example revolves around this problem only, where we show how to merge multiple LiveData objects in Android.

By using MediatorLiveData and Transformations

In this example, we will combine two sources of data and both of them would be served by a LiveData object at the Android activity. To achieve this merging of Android LiveData we would be using MediatorLiveData and MutableLiveData classes extensively. In brief MediatorLiveData allows us to observe on multiple LiveData objects and react to their changes. For this android merged LiveData example one should have a clear understanding of LiveData and its types, therefore please refer to the official documentation for that.

At times the use case is such that a LiveData object needs to be changed into a different type of object. Such cases can be gracefully handled by the LiveData Transformations. In this LiveData merging sample, we will be using Transformations class, this will help us in transforming and merging the LiveData from Room database with LiveData from the API call. To be specific in this sample we will be using Transformations.switchMap type of transformations method which allows us to call a function and transform the data as per our needs. For full details on transformations and room database please read about them in the official documentation, as discussing them in this post would go in a different direction.

How is a merged Android LiveData object created?

Now that we have a clear understanding of how all the components would work, lets have an understanding how they will work together. We want to achieve a functionality where we can get a merged Android LiveData from two or more sources of LiveData. Here one source can be of an API LiveData and the other one can be of a room database. To do so in this example we would use retrofit as a networking client and get a LiveData response. We will also create a stream of LiveData from room database and finally merge the two android LiveData objects with respect to each other by using MediatorLiveData and Transformations.switchMap functions. Once this is done we can put an observer on the final output of the MediatorLiveData object and fulfill our motives. Lets explore some code part in the next sections to have full understanding.

Merging Android LiveData

To showcase how to combine multiple android LiveData objects we will build a sample where we would enter a location in an EditText and show places data for that location. To fetch the surrounding places we would use the Foursqaure venues API in this case. The surrounding places that we fetched would be displayed in a RecyclerView on the same screen. To make this problem a little more interesting, we would also allow the user to mark a place favorite by tapping on a button on the details screen. To do so we will keep the ‘favorite marked place’ in our local Room Database.

Now that the final data on the listing screen would be displayed from two sources i.e. from the local Room database and from a web API call we would need to merge the data from two LiveData sources. To do so we will use MediatorLiveData and Transformations.switchMap, by following full MVVM architecture.

We will use retrofit as an API client in this Android LiveData merging sample. We have defined an interface by the name of FSInterface to make the API call, you can find the full source code at the end of the tutorial. Also we have defined a room database to store the favorite flag, with respect to the data from API. We intend to store this flag in the room database when user marks a place as favorite. Both these data sets would be eventually merged in the repository and passed on to a view model so that it can be displayed on a view i.e. our activity shown above. So lets start by defining the favorite entity:

package com.truiton.mergedlivedata.entity

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "favourite_table")
class Favourite(@PrimaryKey @ColumnInfo(name = "foursquareId") var foursquareId: String,
    @ColumnInfo(name = "fav") var favourite: Boolean)

Next lets define the DAO functions for fetching and modifying the data from database:

package com.truiton.mergedlivedata.dao

import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.truiton.mergedlivedata.entity.Favourite

@Dao
interface FavouriteDao {

    @Query("SELECT * from favourite_table")
    fun getAllFavourites(): LiveData<List<Favourite>>

    @Query("SELECT * from favourite_table WHERE foursquareId LIKE :id")
    fun getFavouritesById(id: String): List<Favourite>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(favourite: Favourite)

    @Query("DELETE FROM favourite_table")
    fun deleteAll()
}

You can find the full implementation for room database, retrofit client and interfaces in the GitHub link shared at the end of the tutorial. Next we can define the repository where we will generate a merged Android LiveData by combining mentioned two LiveData sources:

package com.truiton.mergedlivedata

import androidx.annotation.WorkerThread
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import com.truiton.mergedlivedata.dao.FavouriteDao
import com.truiton.mergedlivedata.entity.Favourite
import com.truiton.mergedlivedata.model.Venues
import com.truiton.mergedlivedata.rest.FSInterface
import com.truiton.mergedlivedata.rest.RetrofitService


class FourSquareRepo(private val favouriteDao: FavouriteDao) {

    val allVenues: MutableLiveData<List<Venues>> = MutableLiveData()
    var foursquareApi = RetrofitService.createService(FSInterface::class.java)
    val mediatorLiveData = MediatorLiveData<List<Venues>>()
    var roomLiveData: LiveData<List<Venues>> = MutableLiveData()

    init {
        allVenues.value = emptyList()

        mediatorLiveData.addSource(allVenues) {
            mediatorLiveData.value = it
        }

        roomLiveData = Transformations.switchMap(favouriteDao.getAllFavourites()) { favs -> getVenues(favs) }
        mediatorLiveData.addSource(roomLiveData) {
            mediatorLiveData.value = it
        }
    }

    private fun getVenues(favourites: List<Favourite>?): LiveData<List<Venues>>? {
        val venues: MutableLiveData<List<Venues>> = MutableLiveData()
        var list = ArrayList<Venues>(allVenues.value!!)
        for (fav in favourites!!) {
            for (venue in list) {
                if (fav.foursquareId == venue.id) {
                    venue.favourite = fav
                }
            }
        }
        venues.value = list
        return venues
    }


    @WorkerThread
    suspend fun insert(favourite: Favourite) {
        favouriteDao.insert(favourite)
    }

    @WorkerThread
    suspend fun getNearbyPlaces(query: String) {
        val foursquareResponse =
            foursquareApi.getPlacesList(
                "your client_id",
                "your client_secret",
                "Seattle,+WA",
                query,
                "20180401",
                "20"
            )
        if (foursquareResponse.isSuccessful) {
            var list = foursquareResponse.body()!!.response.venues.toMutableList()
            for (venue in list) {
                if (favouriteDao.getFavouritesById(venue.id).isNotEmpty()) {
                    venue.favourite = favouriteDao.getFavouritesById(venue.id)[0]
                }
            }
            allVenues.postValue(list)
        } else {
            allVenues.postValue(emptyList())
        }

    }
}

As you can see in the above piece of code we have generated a merged LiveData object mediatorLiveData by merging allVenues data and roomLiveData which are responses from the API call and room database respectively. To do so we have used Transformations.switchMap, which helped us in calling a function in the repository itself through which we modified the structure of every object which being returned in LiveData, without putting an observer. The advantage of using a switchMap here for merging android LiveData objects is that it returns a LiveData object. To actually use this piece of code and display data on the screen please have a look at the full source code at the GitHub link below.

You can see in the full source code that we have used MutableLiveData objects, transformations and MediatorLiveData to merge and pass on the data. To see the merged LiveData in action you need to implement the ViewModel class along with an Activity with all the components for a RecyclerView. In this Android LiveData merging example we have tried to build a sample with all the latest architecture principles, if you wish to implement similar functionality in a different architecture, feel free to do so.

In this Android merged LiveData example we merged two LiveData sources, one from a web API and the other one from a local database, but in real world scenarios there could be situation where you would want to merge more than two LiveData objects. Let me assure you and say all those scenarios can be achieved by using transformations and the approach mentioned in this tutorial. Also it is not possible to cover all the capabilities of LiveData in a single article as there are infinite use-cases where it can be used. For example merging error and success LiveData responses in a single stream or combining different object types in a single stream of LiveData. For more such use-cases stay connected with us on twitter, and Facebook, also if you like this tutorial please share it with your friends and subscribe to our newsletter.

About Mohit Gupt

An android enthusiast, and an iPhone user with a keen interest in development of innovative applications.

Leave a comment

Your email address will not be published. Required fields are marked *