我需要通过链接多个改装API调用来创建一个可观察对象。
我有2个服务: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用于具体的Item id:
{
"id": 1,
"image_url": "http://.../1.png",
"description": "description of item one",
"category": "cat_1",
"quantity" 10
}
这两个API有不同的Base URL。那么,如何才能使一个可观察对象返回的项目列表与他们的图像,数量和类别?
我被困在这里了。这是我的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列表。
要实现您的目标,您应该遵循以下算法:
- 从服务器请求项目列表。现在你的可观察对象发出
List<Item>
; - 将
List<Item>
的发射变换为只发射Item
; - 对于
Item
的每个实例,您应该请求ItemDetail
的实例。由于mItemDetailsService.getItemDetails(@Path("id") int id)
返回Observable<ItemDetail>
,因此您应该使用flatMap(...)
; - 在列表中合并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();
}
})
如果我理解正确的话,那么这应该给你你需要的:
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,以使我的意思更清楚
getItems().flatMap(
// ...
return Observable.from(list);
// ...
).flatMap(
// ...
getItemDetails(id)
// ...
).toList();