Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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??

like image 812
user3354265 Avatar asked Jun 16 '18 14:06

user3354265


2 Answers

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!

like image 78
Oleksiy Martynov Avatar answered Oct 24 '22 09:10

Oleksiy Martynov


This will allow you to have 3 liveData zipped in one. It's easy to know what to do if you need more than 3.

fun <A,B,C> zippedLiveData(a: LiveData<A>, b: LiveData<B>, c: LiveData<C>): LiveData<Pair<A, Pair<B,C>>> {
        return MediatorLiveData<Pair<A, Pair<B,C>>>().apply {
            var lastA: A? = null
            var lastB: B? = null
            var lastC: C? = null

            fun update() {
                val localLastA = lastA
                val localLastB = lastB
                val localLastC = lastC
                if (localLastA != null && localLastB != null && localLastC != null)
                    this.value = Pair(localLastA, Pair(localLastB, localLastC))
            }

            addSource(a) {
                lastA = it
                update()
            }
            addSource(b) {
                lastB = it
                update()
            }
            addSource(c) {
                lastC = it
                update()
            }
        }
    }
like image 25
Vince Avatar answered Oct 24 '22 10:10

Vince