概述
Returns an Observable that emits the results of a specified combiner function applied to combinations of two items emitted,
in sequence, by two other Observables.
流程图:
简单来说zip操作符就是合并多个数据流,
然后发送(Emit)最终合并的数据。
需求描述:
- 在很多app种都会有图片上传的功能,比如商品的评价,
- 客户端允许用户拍照上传(可能多张),
- 把图片上传到又拍云(现在很多中小型公司都是用又拍云作为图片服务器),
- 然后获取图片的url,再把图片的信息(图片url,图片大小)发送给图片。
主要逻辑:
- 先把所有的图片上传到又拍云(比如3张图片)
- 获取图片的url路径,图片大小等
- 最后把数据全部提交给服务器
//需要上传的图片
Picture[] ps = xxx;
Observable.zip(
Observable.from(ps),
getUpYunAddress(ps.length),//获取上传的url
new Func2<Picture, UpYunAddress, Picture>() {
@Override
public Picture call(Picture picture, UpYunAddress upYunAddress) {
//如果该图片已经上传则不应该上传
if (TextUtils.isEmpty(picture.getSource())) {
try {
//使用又拍云提供的工具类,上传图片
String path = UpYunUtil.uploadImage(upYunAddress, picture.getLocalUrl());
//获取最终的url
String finalUrl = upYunAddress.getPrefix() + path;
picture.setSource(finalUrl);
} catch (Exception e) {
e.printStackTrace();
}
}
return picture;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
//上传成功后获取图片大小
.flatMap(new Func1<Picture, Observable<Picture>>() {
@Override
public Observable<Picture> call(Picture picture) {
if (TextUtils.isEmpty(picture.getHeight()) || TextUtils.isEmpty(picture.getWidth())) {
BitmapFactory.Options options;
if (!TextUtils.isEmpty(picture.getLocalUrl())) {
options = ImageUtil.getBitmapOptions(picture.getLocalUrl());
picture.setLocalUrl(null);
} else {
options = ImageUtil.getBitmapOptions(picture.getSource());
}
picture.setWidth(String.valueOf(options.outWidth));
picture.setHeight(String.valueOf(options.outHeight));
}
return Observable.just(picture);
}
});
//最后处理最终的数据。
扩展
假设这样一种场景,我们利用github api开发一个app,在user界面,我既要请求user基本信息,又要列举user下的event数据,为此,我准备使用Retrofit来做网络请求。
首先写好interfaces:
public interface GitHubUser {
@GET("users/{user}")
Observable<JsonObject> getUser(@Path("user") String user);
}
public interface GitHubEvents {
@GET("users/{user}/events")
Observable<JsonArray> listEvents(@Path("user") String user);
}
然后定义好我们的两个Observable:
Retrofit repo = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
Observable<JsonObject> userObservable = repo
.create(GitHubUser.class)
.getUser(loginName)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
Observable<JsonArray> eventsObservable = repo
.create(GitHubEvents.class)
.listEvents(loginName)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
分别是userObservable和eventsObservable,很显然的,我们将会需要两次请求。两次就两次嘛,但是这里有个问题。
虽然在后台有两次请求,但是在前台,我们希望用户打开这个页面,然后等待加载,然后显示。用户只有一次等待加载的过程。所以说,我们需要等待这两个请求都返回结果了,再开始显示数据。
怎么办?自己写判断两个都加载已完成的代码吗?逻辑好像也不是很复杂,但是代码看起来就没有那么高大上了啊。
其实既然你都用过了还有,那么直觉上你应该意识到也许RxJava可以解决这个问题。没错,就是RxJava,使用zip操作符。
zip( ):使用一个函数组合多个Observable发射的数据集合,然后再发射这个结果
“Retrofit对Observable的支持使得它可以很简单的将多个REST请求结合起来。比如我们有一个请求是获取照片的,还有一个请求是获取元数据的,我们就可以将这两个请求并发的发出,并且等待两个结果都返回之后再做处理:
Observable<UserAndEvents> combined = Observable.zip(userObservable, eventsObservable, new Func2<JsonObject, JsonArray, UserAndEvents>() {
@Override
public UserAndEvents call(JsonObject jsonObject, JsonArray jsonElements) {
return new UserAndEvents(jsonObject, jsonElements);
}
});
combined.subscribe(new Subscriber<UserAndEvents>() {
...
@Override
public void onNext(UserAndEvents o) {
// You can access the results of the
// two observabes via the POJO now
}
});
public class UserAndEvents {
public UserAndEvents(JsonObject user, JsonArray events) {
this.events = events;
this.user = user;
}
public JsonArray events;
public JsonObject user;
}
这里的UserAndEvents是我们自己定义的,目的是把原来的两个结果的信息柔和在一个对象中,毕竟新的Observable也只能返回一次。