Search code examples
javaandroidrx-javarx-androidreactivex

RxJava Android zip many requests with different types


I have a bit "estetic" problem with Observable.zip function. You use it like this:

Observerbale.zip(
    reqA, reqB, reqC,
    (a, b, c) -> {/*deal with them*/});

The amount of requests equals amount of params in "deal-with-it" function. Now, if you have more requests, like 6, you'll end up with function, that takes 6 arguments (let assume, that all of them have different types). It just doesn't feel clean. Is there a way to wrap them in one class, as e.g. attributes?

My real-life problem right now is, that I use zip, to load setup data:

Observable.zip(
    loadAPIMenus(),                        //1.
    databaseService.readFavorites(),       //2.
    (menuResponse, favorites) -> loadFavorites(menuResponse, favorites)) //3.
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(
        menus -> menuSubscriber.onNext(menus), //4.
        error -> menuSubscriber.onError(error));
  1. loads items for list view from rest api
  2. loads ids of those items, which are stored as favorite in db
  3. merges both lists, so items, which are favorite, are have isFavorite >set to true
  4. Updates list view

It's not that bad now. But I'ld like to add 2-3 requests for other data end function at #3 will grow to 4-line mammoth with too many function params.

I figured, that I can use Observable.zip nested one in another, but may be dangerous. Is there any more ellegant way to wrap those params?

I'ld be glad to see your suggestions.


Solution

  • In the docs for Observable::zip(), it states:

    "You can provide the Observables to be zipped together to zip either as between two and nine individual parameters, or as a single parameter: either an Iterable of Observables or an Observable that emits Observables (as in the illustration above)."

    That opens the door to using some form of Iterable<Observable<T>>, or Observable<Observable<T>> instead of the overloads that take anywhere between 2-9 Observables that you're using now.

    With this approach, the last parameter to zip also now becomes FuncN, which is called with an argument list Object... args as its parameter, which you can treat like an array.

    If you use the Observable::zip(Iterable, FuncN) approach, you could:

    List<Observable<T>> list = new ArrayList<>();
    list.add(req1);
    list.add(req2);
    
    Observable.zip(list, new FuncN<???>() {
        @Override
        public ??? call(Object... args) {
            return ???;
        }
    );
    

    I wouldn't bother with the Observable<Observable<T>> in your case since that requires you to create a list or array of observable to pass to Observable::from() in the first place, although there might be a better way to do that.