【问题标题】:RxJava + Retrofit 2 chaining API requestsRxJava + Retrofit 2 链接 API 请求
【发布时间】:2016-10-20 13:53:12
【问题描述】:

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

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

物品服务:

@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"
  }
]

具体项目 ID 的 ItemDetails json:

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

这 2 个 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?
  • 对不起,你能举个例子吗?

标签: android rx-java retrofit2 dagger-2


【解决方案1】:

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

  1. 从服务器请求项目列表。现在你的 observable 发出 List&lt;Item&gt;;
  2. List&lt;Item&gt; 的发射转换为仅发射Item
  3. 对于Item 的每个实例,您应该请求ItemDetail 的实例。因为mItemDetailsService.getItemDetails(@Path("id") int id)返回Observable&lt;ItemDetail&gt;,所以你应该使用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();
        }
     })

【讨论】:

    【解决方案2】:

    如果我理解正确,那么这应该可以满足您的需求:

    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并添加到@987654325 @ 列表。然后使用zip 运算符,它基本上启动所有请求并在所有请求完成后触发FuncN 中的回调。您必须在那里将Object 转换为ItemDetail

    编辑:我在方法名称前添加了 service1 和 service2 以便更清楚我的意思。

    【讨论】:

    • getItemDetails 返回一个Object,这是两个不同的Service
    • 可能是我的问题不正确,我已经编辑了我的帖子。
    • 我在答案的末尾添加了一个小解释,这可能会推动您朝着正确的方向前进。
    • 好的,我想我理解这种方法,但是在实现上有一些问题。我已经更新了我的问题。
    【解决方案3】:
    getItems().flatMap(
        // ...
        return Observable.from(list);
        // ...
    ).flatMap(
        // ...
        getItemDetails(id)
        // ...
    ).toList();
    

    【讨论】:

      猜你喜欢
      • 2016-08-15
      • 1970-01-01
      • 1970-01-01
      • 2016-07-31
      • 2021-11-07
      • 1970-01-01
      • 1970-01-01
      • 2021-09-04
      • 2015-07-26
      相关资源
      最近更新 更多