OOZOU
Get in Touch
Back to Blog

LiveData to the next level with MediatorLiveData and Transformations

An introduction to MediatorLiveData and Transformations to build reactive patterns with LiveData.

December 7, 2018
4 min read
LiveData to the next level with MediatorLiveData and Transformations

MediatorLiveData

From its explanation, it is a subclass of liveData which can ‘observe’ other LiveData and trigger onChanged() method when observed LiveData’s value is changed.

LiveData<String> source = ....; //
final MediatorLiveData<String> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<String>() {
            @Override
            public void onChanged(@Nullable String x) {
                // do something here 
            }
        });
lang-java

From the sample code above, when source’s value is changed, the onChanged method of Observer will be triggered. Transformations**

This class is way more like an utilisation of MediatorLiveData. It contains only 2 methods which are map() and switchMap()

@MainThread
    public static <X, Y> LiveData<Y> map(@NonNull LiveData<X> source,
            @NonNull final Function<X, Y> func) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() {
            @Override
            public void onChanged(@Nullable X x) {
                result.setValue(func.apply(x));
            }
        });
        return result;
    }

   @MainThread
    public static <X, Y> LiveData<Y> switchMap(@NonNull LiveData<X> trigger,
            @NonNull final Function<X, LiveData<Y>> func) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(trigger, new Observer<X>() {
            LiveData<Y> mSource;

            @Override
            public void onChanged(@Nullable X x) {
                LiveData<Y> newLiveData = func.apply(x);
                if (mSource == newLiveData) {
                    return;
                }
                if (mSource != null) {
                    result.removeSource(mSource);
                }
                mSource = newLiveData;
                if (mSource != null) {
                    result.addSource(mSource, new Observer<Y>() {
                        @Override
                        public void onChanged(@Nullable Y y) {
                            result.setValue(y);
                        }
                    });
                }
            }
        });
        return result;
    }
lang-java

These 2 methods doing the same thing which apply a function on the value stored in the LiveData object, and propagates the result downstream but the switchMap(), the function that passed in must return a LiveData object.

I’ve created a sample project here. You can take a look and stay with me to let me explain how the code works.

I use https://jsonplaceholder.typicode.com/ as a test api with /todos/1 endpoint. There is a created model name Todo to match with the response

import com.google.gson.annotations.SerializedName

// https://jsonplaceholder.typicode.com/todo/1
class Todo(
    @SerializedName("userId") val userId: Int,
    @SerializedName("id") val id: Int,
    @SerializedName("title") val title: String,
    @SerializedName("completed") val completed: Boolean
)
lang-kotlin

There are 2 more classes for handling responses from api.

import android.arch.lifecycle.LiveData

data class ResponseError(val code: Int? = null, val messages: String? = null)

data class ResponseResult<T>(val data: LiveData<T>, val error: LiveData<ResponseError>)
lang-kotlin

The ResponseResult will have 2 properties, both are a LiveData, one is for the response, the other one is for the error (ResponseError).

According to the Google suggestion, I made a repository module with will take a responsibility to fetch a data from network. In this module, I did some trick with ResponseResult:

class TodoRepository(private val api: TodoApi) {

    fun getTodos(): ResponseResult<Todo> {
        val data = MutableLiveData<Todo>()
        val error = MutableLiveData<ResponseError>()
        api.getTodos().enqueue(object : Callback<Todo> {
            override fun onResponse(call: Call<Todo>, response: Response<Todo>) {
                if (response.isSuccessful) {
                    data.value = response.body()
                } else {
                    error.value = ResponseError(response.code(), response.errorBody()?.string())
                }
            }

            override fun onFailure(call: Call<Todo>, t: Throwable) {
                error.value = ResponseError()
            }
        })
        return ResponseResult(data, error)
    }
}
lang-kotlin

The function getTodos() will return ResponseResult<Todo>, its 2 properties, data’s value will be updated when response is finished, and error’s value will be updated when error happened.

By following the SOLID guideline, I keep my Usecase class so simple.

class GetTodoUsecases(private val repository: TodoRepository) {

    operator fun invoke() = repository.getTodos()
}
lang-kotlin

Chaining LiveData**

Let’s have a look at my ViewModel and Fragment class:

class MainFragment: Fragment(){
    ...
    ...
    ...
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel._onActivityCreated.call()
    }
    ...
    ...
    ...
}

class MainViewModel(private val getTodoUsecase: GetTodoUsecases) : ViewModel() {

    val _onActivityCreated = SingleLiveEvent<Any>()

    private val responseResult: LiveData<ResponseResult<Todo>> = Transformations.map(_onActivityCreated) {
        getTodoUsecase()
    }

    val todoResult: LiveData<Todo> = Transformations.switchMap(responseResult) {
        it.data
    }

    val errorResult: LiveData<ResponseError> = Transformations.switchMap(responseResult) {
        it.error
    }
}
lang-kotlin

I triggered a LiveData (SingleLiveEvent) at onActivityCreated(), and if you take a look at line 18, Transformation.map() is called which mean getTodoUsecase() will be called. After fetching is done, responseResult will be assigned. And if you take a look again at line 22 and 26, Transformation.switchMap() are called which means when the responseResult’s value is updated, todoResult and errorResult instances will be pointed to the same instances as _responseResult’s properties. _

Tips: OnChanged() method will not be called if MediatorLiveData is not observed at anywhere.

Reference:

https://stackoverflow.com/questions/47575961/what-is-the-difference-between-map-and-switchmap-methods

Related Articles

How Can I Design a Software: A Beginners Guide for Business Owners

How Can I Design a Software: A Beginners Guide for Business Owners

Designing software is an exciting yet complex process that involves creativity, logical thinking, and problem-solving skills.

Read More →
The Future of Edge Computing: What Businesses Need to Know

The Future of Edge Computing: What Businesses Need to Know

The digital landscape is evolving rapidly, and businesses must adapt to keep up with rising demands for speed, efficiency, and real-time processing.

Read More →
The Future of Retail: AI, AR, and Digital Transformation Trends

The Future of Retail: AI, AR, and Digital Transformation Trends

Retail is evolving at an unprecedented pace, driven by AI (Artificial Intelligence), AR (Augmented Reality), and digital transformation technologies, revolutionizing the retail industry. Traditional brick-and-mortar stores are no longer just about selling products—they are becoming smart, interactiv

Read More →

Have a Project in Mind?

Let's discuss how we can help bring your ideas to life with our expertise in web and mobile development.

Start a Conversationhello@oozou.com
OOZOU Logo

Bangkok · Singapore · Hong Kong

X
Facebook
Instagram
LinkedIn

Services

  • Web Development
  • AI Agents & Generative AI
  • Mobile App Development
  • Data Analytics & Engineering
  • UI/UX & Product Design
  • Digital Transformation

Company

  • About Us
  • Careers
  • Blog
  • Whitepapers
  • Case Studies
  • Today I Learned
  • Contact

More

  • Industries
  • Get an Estimate
  • Partner Network
© 2026 OOZOU. All rights reserved.
Privacy PolicyCode of ConductABAC Policy