Search code examples
javaandroidandroid-architecture-componentsandroid-livedata

Zip 4 or more async calls using livedata


Is there any possible way of using livedata to run multiple async calls in parallel??

Let say I have 4 async calls. I want to wait until everything is done and later use the result from all the 4 calls accordingly.

One way I can think of is this

public class MakeParallel {
    private final CountDownLatch countDown = new CountDownLatch(4);

    public void firstCall() {
        Transformation.map(makeFirstCall(), input -> {
            if(input.isSuccessful()) {
                countDownLatch.countDown();
                checkResult();
            }
            return input;
        } 
    }

    public void secondCall() {
        Transformation.map(makeSecondCall(), input -> {
            if(input.isSuccessful()) {
                countDownLatch.countDown();
                checkResult();
            }
            return input;
        } 
    }

    void checkResult() {
        if(countDownLatch.getCount == 0) {
            // Result is ready
        } else {
            // Something has error
        }
    }
}

Is there any better way to resolve this scenario??


Solution

  • So the trick is to use MediatorLiveData and have it observe each LiveData object and zip the changes into a collection of some sort.

    public static LiveData<ArrayList<Object>> zipLiveData(LiveData<Object>... liveItems){
        final ArrayList<Object> zippedObjects = new ArrayList<>();
        final MediatorLiveData<ArrayList<Object>> mediator = new MediatorLiveData<>();
        for(LiveData<Object> item: liveItems){
            mediator.addSource(item, new Observer<Object>() {
                @Override
                public void onChanged(@Nullable Object o) {
                    if(!zippedObjects.contains(o)){
                        zippedObjects.add(o);
                    }
                    mediator.setValue(zippedObjects);
                }
            });
        }
        return mediator;
    }
    

    Or in Kotlin:

    fun zipLiveData(vararg liveItems: LiveData<*>): LiveData<ArrayList<Any>> {
    return MediatorLiveData<ArrayList<Any>>().apply {
        val zippedObjects = ArrayList<Any>()
        liveItems.forEach {
            addSource(it, { item ->
                if (! zippedObjects.contains(item as Any)) {
                    zippedObjects.add(item)
                }
                value = zippedObjects
            })
        }
    }}
    

    This solution has no type safety. Feel free to customize to your needs!