RxJava + Retrofit 2链接API请求

时间:2016-10-20 13:53:13

标签: android rx-java retrofit2 dagger-2

我需要通过将多个改进的API调用链接在一起来创建一个observable。

我有2个服务:ItemService只包含项目名称和ID,ItemDetailsS​​ervice包含很多关于项目的详细信息,如描述图像等。我正在使用Retrofit 2 + RxJava + Dagger 2.

ItemService:

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

ItemDetailsS​​ervice:

@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 for concret Item id:

  {
    "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列表。

3 个答案:

答案 0 :(得分:2)

要实现目标,您应该遵循下一个算法:

  1. 请求服务器中的项目列表。现在您的observable发出List<Item>;
  2. List<Item>的发射转换为仅发出Item;
  3. 对于Item的每个实例,您应该请求ItemDetail的实例。由于mItemDetailsService.getItemDetails(@Path("id") int id)返回Observable<ItemDetail>,因此您应该使用flatMap(...);
  4. 组合ItemDetail响应列表。
  5. 代码:

    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();
            }
         })
    

答案 1 :(得分: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,以便更清楚我的意思。

答案 2 :(得分:0)

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