How can I perform LiveData transformations on a background thread?

It may be eaiser to do using MediatorLiveData. Transformations.map() is implemented with MediatorLiveData under the hood.

@MainThread
public static <X, Y> LiveData<Y> mapAsync(
  @NonNull LiveData<X> source,
  @NonNull final Function<X, Y> mapFunction) {
  final MediatorLiveData<Y> result = new MediatorLiveData<>();
  result.addSource(source, new Observer<X>() {
    @Override
    public void onChanged(@Nullable final X x) {
      AsyncTask.execute(new Runnable() {
        @Override
        public void run() {
          result.postValue(mapFunction.apply(x));
        }
      });
    }
  });
  return result;
}

Listen to a MediatorLiveData<T> that listens to two other LiveData<T>s.

For example:

val exposed: LiveData<List<T>> = MediatorLiveData<List<T>>().apply {
    addSource(aLiveDataToMap) { doWorkOnAnotherThread(it) }
    addSource(aMutableLiveData) { value = it }
}

private fun doWorkOnAnotherThread(t: T) {
    runWorkOnAnotherThread {
        val t2 = /* ... */
        aMutableLiveData.postValue(t2)
    }
}

Whenever aLiveDataToMap changes, it will trigger doWorkOnAnotherThread() which will then set the value of aMutableLiveData, which finally sets to value of exposed, which a lifecycle-owner will be listening to. Replace Ts with your desired type.


  • The original, “source” LiveData can be monitored by a new Observer instance.
  • This Observer instance, when source LiveData is emitted, can prepare a background thread to perform the needed transformation and then emit it via a new, “transformed” LiveData.
  • The transformed LiveData can attach the aforementioned Observer to the source LiveData when it has active Observers, and detach them when it doesn't, ensuring that the source LiveData is only being observed when necessary.

The question gives an example source LiveData<List<MyDBRow>> and needs a transformed LiveData<List<MyRichObject>>. A combined transformed LiveData and Observer could look something like this:

class MyRichObjectLiveData
        extends LiveData<List<MyRichObject>>
        implements Observer<List<MyDBRow>>
{
    @NonNull private LiveData<List<MyDBRow>> sourceLiveData;

    MyRichObjectLiveData(@NonNull LiveData<List<MyDBRow>> sourceLiveData) {
        this.sourceLiveData = sourceLiveData;
    }

    // only watch the source LiveData when something is observing this
    // transformed LiveData
    @Override protected void onActive()   { sourceLiveData.observeForever(this); }
    @Override protected void onInactive() { sourceLiveData.removeObserver(this); }

    // receive source LiveData emission
    @Override public void onChanged(@Nullable List<MyDBRow> dbRows) {
        // set up a background thread to complete the transformation
        AsyncTask.execute(new Runnable() {
            @Override public void run() {
                assert dbRows != null;
                List<MyRichObject> myRichObjects = new LinkedList<>();
                for (MyDBRow myDBRow : myDBRows) {
                    myRichObjects.add(MyRichObjectBuilder.from(myDBRow).build());
                }
                // use LiveData method postValue (rather than setValue) on
                // background threads
                postValue(myRichObjects);
            }
        });
    }
}

If multiple such transformations are needed, the above logic could be made generic like this:

abstract class TransformedLiveData<Source, Transformed>
        extends LiveData<Transformed>
        implements Observer<Source>
{
    @Override protected void onActive()   { getSource().observeForever(this); }
    @Override protected void onInactive() { getSource().removeObserver(this); }

    @Override public void onChanged(@Nullable Source source) {
        AsyncTask.execute(new Runnable() {
            @Override public void run() {
                postValue(getTransformed(source));
            }
        });
    }

    protected abstract LiveData<Source> getSource();
    protected abstract Transformed getTransformed(Source source);
}

and the subclass for the example given by the question could look something like this:

class MyRichObjectLiveData
        extends TransformedLiveData<List<MyDBRow>, List<MyRichObject>>
{
    @NonNull private LiveData<List<MyDBRow>> sourceLiveData;

    MyRichObjectLiveData(@NonNull LiveData<List<MyDBRow>> sourceLiveData) {
        this.sourceLiveData = sourceLiveData;
    }

    @Override protected LiveData<List<MyDBRow>> getSource() {
        return sourceLiveData;
    }

    @Override protected List<MyRichObject> getTransformed(List<MyDBRow> myDBRows) {
        List<MyRichObject> myRichObjects = new LinkedList<>();
        for (MyDBRow myDBRow : myDBRows) {
            myRichObjects.add(MyRichObjectBuilder.from(myDBRow).build());
        }
        return myRichObjects;
    }
}

A solution with coroutines:

class RichLiveData(val rows: LiveData<List<MyDBRow>>) : LiveData<List<MyRichObject>>(),
        CoroutineScope by CoroutineScope(Dispatchers.Default) {

    private val observer = Observer<List<MyDBRow>> { rows ->
        launch {
            postValue(/*computationally expensive stuff which returns a List<MyRichObject>*/)
        }
    }

    override fun onActive() {
        rows.observeForever(observer)
    }

    override fun onInactive() {
        rows.removeObserver(observer)
    }
}