RxJava + Retrofit 2 链式API请求

6

我需要通过链接多个retrofit API调用来创建一个可观察对象。

我有两个服务:ItemService仅包含项目名称和ID,而ItemDetailsService包含关于项目的许多其他详细信息,例如描述图像等。 我正在使用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);

项目的JSON:

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

一个具体物品的ItemDetails json:

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

这两个API有不同的基础URL,那么如何创建一个Observable,返回包含图片、数量和类别的项目列表?
更新:
我卡在这里了。这是我的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);
    }

}

我不太明白如何正确地制作。如果我只需要列表中某些项目的少量详细信息而不是从ItemDetail获得全部内容,那么我该怎么办?
我只需要将ItemDetail中的一些详细信息添加到Items列表中。

嗯... flatMap - EpicPandaForce
1
抱歉,您能提供一些示例吗? - STK90
3个回答

3
为了实现您的目标,您应该按照以下算法进行操作:
  1. 从服务器请求项目列表。现在您的可观察对象会发出 List<Item>
  2. 将发射 List<Item> 转换为仅发射 Item
  3. 对于每个 Item 实例,您应该请求 ItemDetail 实例。由于 mItemDetailsService.getItemDetails(@Path("id") int id) 返回 Observable<ItemDetail>,因此您应该使用 flatMap(...) 进行操作;
  4. 将 ItemDetail 响应组合成列表。
代码:
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();

如果您只需要图片链接而非整个商品详情,您可以调用以下代码:

image_url

.map(new Func1<ItemDetail, String>() {
     @Override
        public String call(Object itemDetail) {
          return itemDetail.getImageUrl();
        }
     })

0
getItems().flatMap(
    // ...
    return Observable.from(list);
    // ...
).flatMap(
    // ...
    getItemDetails(id)
    // ...
).toList();

0
如果我理解你的意思正确的话,那么这应该可以给你所需的东西:
private Observable<List<ItemDetail>> getItemDetails() {
    return service1.getItems()
            .flatMap(new Func1<List<Item>, Observable<List<ItemDetail>>>() {
                @Override
                public Observable<List<ItemDetail>> call(List<Item> items) {
                    List<Observable<ItemDetail>> detailObservables = new ArrayList<>();
                    for (Item item : items) {
                        detailObservables.add(service2.getItemDetails(item.id));
                    }
                    return Observable.zip(
                            detailObservables,
                            new FuncN<List<ItemDetail>>() {
                                @Override
                                public List<ItemDetail> call(Object... args) {
                                    List<ItemDetail> itemDetails = new ArrayList<>();
                                    for (Object arg : args) {
                                        itemDetails.add((ItemDetail) arg);
                                    }
                                    return itemDetails;
                                }
                            }
                    );
                }
            });
}

请注意,我在这段代码中假设您的getItemDetails方法返回一个ItemDetail对象列表,仅为了代码清晰度。
解释:我使用了flatMap()操作符来处理从第一个服务请求接收到的项目列表,然后为每个项目详细信息API请求创建了一个Observable,并将其添加到detailObservables列表中。然后使用zip操作符,它基本上启动所有请求,并在所有请求完成时触发FuncN中的回调。您必须在那里将Object强制转换为ItemDetail
编辑:我在方法名称前面添加了service1和service2,以使我的意思更加清晰。

getItemDetails返回一个对象,这是两个不同的服务。 - STK90
也许我的问题之前不正确,我已经编辑了我的帖子。 - STK90
我在答案的结尾添加了一个小解释,可能会引导您朝正确的方向前进。 - ehehhh
好的,我想我理解了这个方法,但在实现过程中遇到了一些问题。我已经更新了我的问题。 - STK90

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接