Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RxJava + Retrofit 2 chaining API requests

I need to create an observable by chaining together multiple retrofit API calls.

I have 2 services: ItemService which contains only item name and id, and ItemDetailsService which contains many other details about item like description image and etc. I am using Retrofit 2 + RxJava + Dagger 2.

ItemService:

@GET("apione/items")
Observable<List<Items>> getItems();

ItemDetailsService:

@GET("apitwo/item/{id}")
Observable<ItemDetail> getItemDetails(@Path("id") int id);

Items json:

[
  {
    "id": 1,
    "name": "one"
  },
  {
    "id": 2,
    "name": "two"
  },
    {
    "id": 3,
    "name": "three"
  }
]

ItemDetails json for a concret Item id:

  {
    "id": 1,
    "image_url": "http://.../1.png",
    "description": "description of item one",
    "category": "cat_1",
    "quantity" 10
  }

Also these 2 API's have different Base URL. So how is it possible to make an Observable wich returns list of items with their image, quantity and category ?

UPDATED

I stucked here. This is my ItemPresenter

public class ItemPresenter extends BasePresenter<ItemView> implements Observer<List<Item>> {

    @Inject protected ItemService mItemService;

    @Inject protected ItemDetailsService mItemDetailsService;

    @Inject
    public ItemPresenter() {

    }

    public void getItems() {
        getView().onShowDialog("Loading");
        Observable<List<Item>> itemObservables= mItemService.getItems()
                .flatMap(new Func1<List<Item>, Observable<ItemDetail>>() {
                    @Override
                    public Observable<ItemDetail> call(List<Item> items) {
                        List<Observable<ItemDetail>> detailObservables = new ArrayList<>();
                        for (Item item : items) {
                            detailObservables.add(mItemDetailsService.getItemDetail(item.getId());
                        }
                        return Observable.zip(
                                detailObservables,
                                args -> {
                                    List<Item> itemDetails = new ArrayList<>();
                                    for (Object arg : args) {
                                        itemDetails.add((Item) arg);
                                    }
                                    return itemDetails;
                                }
                        );
                    }

                });

        subscribe(detailObservables, this);
    }

    @Override
    public void onCompleted() {
        getView().onHideDialog();
        getView().onShowToast("Complete");
    }

    @Override
    public void onError(Throwable e) {
        getView().onHideDialog();
        getView().onShowToast("Error loading " + e.getMessage());
    }

    @Override
    public void onNext(List<Item> items) {
        getView().onItems(items);
    }

}

Dont understand how to make it correctly. And whar I have to do if I need only few details about items in list, not everething from ItemDetail ?

I only need to add some details from ItemDetail to Items list.

like image 383
STK90 Avatar asked Oct 20 '16 13:10

STK90


1 Answers

To achieve your goal you should to follow the next algorithm:

  1. Request list of items from Server. Now your observable emits List<Item>;
  2. Transform emitting of List<Item> to emit just Item;
  3. For each instance of Item you should to request instance of ItemDetail. As mItemDetailsService.getItemDetails(@Path("id") int id) return Observable<ItemDetail>, so you should to use flatMap(...) for this;
  4. Combine ItemDetail responces in list.

Code:

Observable<List<ItemDetail>> detailObservables = mItemService.getItems()
  .flatMap(new Func1<List<Item>, Observable<Item>>() {
     @Override
       public Observable<Item> call(List<Item> items) {
         return Observable.from(items);
       }
     })
     .flatMap(new Func1<Item, Observable<ItemDetail>>() {
        @Override
           public Observable<ItemDetail> call(Item item) {
             return mItemDetailsService.getItemDetail(item.getId());
           }
        }).toList();

If you need for example just image_url instead of whole item detail, you could call:

.map(new Func1<ItemDetail, String>() {
     @Override
        public String call(Object itemDetail) {
          return itemDetail.getImageUrl();
        }
     })
like image 123
Dmitry Isachenko Avatar answered Oct 04 '22 04:10

Dmitry Isachenko