Rxjava Android如何使用Zip运算符

时间:2015-05-13 15:56:54

标签: java android rx-java

我在为我的android项目理解RxJava中的zip操作符时遇到了很多麻烦。 问题 我需要能够发送网络请求来上传视频 然后我需要发送一个网络请求来上传图片 最后我需要添加一个描述并使用前两个请求的响应将视频和图片的位置网址以及描述上传到我的服务器。

我认为zip操作符对于此任务是完美的,因为我知道我们可以接受两个可观察量(视频和图片请求)的响应并将它们用于我的最终任务。 但我似乎无法让我想象它。

我正在寻找有人回答如何通过一些伪代码在概念上完成这个问题。 谢谢

7 个答案:

答案 0 :(得分:63)

Zip运算符严格配对来自observable的发射项。它等待两个(或更多)项目到达然后合并它们。所以是的,这将适合您的需求。

我会使用Func2来连接前两个observable的结果。 请注意,如果您使用Retrofit,这种方法会更简单,因为它的api接口可能会返回一个observable。否则你需要创建自己的observable。

// assuming each observable returns response in the form of String
Observable<String> movOb = Observable.create(...);
// if you use Retrofit
Observable<String> picOb = RetrofitApiManager.getService().uploadPic(...),
Observable.zip(movOb, picOb, 

   new Func2<String, String, MyResult>() {

      @Override
      public MyResult call(String movieUploadResponse, 
          String picUploadResponse) {
            // analyze both responses, upload them to another server
            // and return this method with a MyResult type
          return myResult;
         }
      }
)
// continue chaining this observable with subscriber
// or use it for something else

答案 1 :(得分:17)

example

Observable<String> stringObservable1 = Observable.just("Hello", "World");
Observable<String> stringObservable2 = Observable.just("Bye", "Friends");

Observable.zip(stringObservable1, stringObservable2, new BiFunction<String, String, String>() {
    @Override
    public String apply(@NonNull String s, @NonNull String s2) throws Exception {
        return s + " - " + s2;
    }
}).subscribe(new Consumer<String>() {
    @Override
    public void accept(String s) throws Exception {
        System.out.println(s);
    }
});

这将打印:

Hello - Bye
World - Friends

答案 2 :(得分:2)

这里我有一个例子,我以异步方式使用Zip,以防你好奇

<s:Envelope xmlns:s="http://schemas.xmlso.......

您可以在此处查看更多示例https://github.com/politrons/reactive

答案 3 :(得分:1)

我一直在寻找一个关于如何使用Zip运算符的简单答案,以及如何处理我创建的Observables以将它传递给它,我想知道我是否应该为每个observable调用subscribe(),没有这些答案很容易找到,我必须自己解决,所以这是一个简单的例子在2 Observables上使用Zip运算符:

@Test
public void zipOperator() throws Exception {

    List<Integer> indexes = Arrays.asList(0, 1, 2, 3, 4);
    List<String> letters = Arrays.asList("a", "b", "c", "d", "e");

    Observable<Integer> indexesObservable = Observable.fromIterable(indexes);

    Observable<String> lettersObservable = Observable.fromIterable(letters);

    Observable.zip(indexesObservable, lettersObservable, mergeEmittedItems())
            .subscribe(printMergedItems());
}

@NonNull
private BiFunction<Integer, String, String> mergeEmittedItems() {
    return new BiFunction<Integer, String, String>() {
        @Override
        public String apply(Integer index, String letter) throws Exception {
            return "[" + index + "] " + letter;
        }
    };
}

@NonNull
private Consumer<String> printMergedItems() {
    return new Consumer<String>() {
        @Override
        public void accept(String s) throws Exception {
            System.out.println(s);
        }
    };
}

打印结果是:

[0] a
[1] b
[2] c
[3] d
[4] e

我头脑中的问题的最终答案如下

传递给zip()方法的Observable只需要创建,它们不需要任何订阅者,只创建它们就足够了......如果你想让任何observable在调度程序上运行,你可以为Observable指定这个...我还尝试了Observables上的zip()运算符,他们应该等待那里的结果,并且只有当两个结果都准备就绪时才会触发zip()的消耗(这是预期的行为)

答案 4 :(得分:1)

这是我使用 Single.zip rxJava2

的实现

我试图使其尽可能容易理解

//
// API Client Interface
//
@GET(ServicesConstants.API_PREFIX + "questions/{id}/")
Single<Response<ResponseGeneric<List<ResponseQuestion>>>> getBaseQuestions(@Path("id") int personId);

@GET(ServicesConstants.API_PREFIX + "physician/{id}/")
Single<Response<ResponseGeneric<List<ResponsePhysician>>>> getPhysicianInfo(@Path("id") int personId);

//
// API middle layer - NOTE: I had feedback that the Single.create is not needed (but I haven't yet spent the time to improve it)
//
public Single<List<ResponsePhysician>> getPhysicianInfo(int personId) {
    return Single.create(subscriber -> {
        apiClient.getPhysicianInfo(appId)
                .subscribeOn(Schedulers.io())
                .observeOn(Schedulers.io())
                .subscribe(response -> {
                    ResponseGeneric<List<ResponsePhysician>> responseBody = response.body();
                    if(responseBody != null && responseBody.statusCode == 1) {
                        if (!subscriber.isDisposed()) subscriber.onSuccess(responseBody.data);
                    } else if(response.body() != null && response.body().status != null ){
                        if (!subscriber.isDisposed()) subscriber.onError(new Throwable(response.body().status));
                    } else {
                        if (!subscriber.isDisposed()) subscriber.onError(new Throwable(response.message()));
                    }
                }, throwable -> {
                    throwable.printStackTrace();
                    if(!subscriber.isDisposed()) subscriber.onError(throwable);
                });
    });
}

public Single<List<ResponseQuestion>> getHealthQuestions(int personId){
    return Single.create(subscriber -> {
        apiClient.getBaseQuestions(personId)
                .subscribeOn(Schedulers.io())
                .observeOn(Schedulers.io())
                .subscribe(response -> {
                    ResponseGeneric<List<ResponseQuestion>> responseBody = response.body();
                    if(responseBody != null && responseBody.data != null) {
                        if (!subscriber.isDisposed()) subscriber.onSuccess(response.body().data);
                    } else if(response.body() != null && response.body().status != null ){
                        if (!subscriber.isDisposed()) subscriber.onError(new Throwable(response.body().status));
                    } else {
                        if (!subscriber.isDisposed()) subscriber.onError(new Throwable(response.message()));
                    }
                }, throwable -> {
                    throwable.printStackTrace();
                    if(!subscriber.isDisposed()) subscriber.onError(throwable);
                });
    });
}

//please note that ResponseGeneric is just an outer wrapper of the returned data - common to all API's in this project

public class ResponseGeneric<T> {

    @SerializedName("Status")
    public String status;

    @SerializedName("StatusCode")
    public float statusCode;

    @SerializedName("Data")
    public T data;
}

//
// API end-use layer - this gets close to the UI so notice the oberver is set for main thread
//
private static class MergedResponse{// this is just a POJO to store all the responses in one object
    public List<ResponseQuestion> listQuestions;
    public List<ResponsePhysician> listPhysicians;
    public MergedResponse(List<ResponseQuestion> listQuestions, List<ResponsePhysician> listPhysicians){
        this.listQuestions = listQuestions;
        this.listPhysicians = listPhysicians;
    }
}

// example of Single.zip() - calls getHealthQuestions() and getPhysicianInfo() from API Middle Layer
private void downloadHealthQuestions(int personId) {
    addRxSubscription(Single
            .zip(getHealthQuestions(personId), getPhysicianInfo(personId), MergedResponse::new)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(response -> {
                if(response != null) {
                    Timber.i(" - total health questions downloaded %d", response.listQuestions.size());
                    Timber.i(" - physicians downloaded %d", response.listPhysicians.size());

                    if (response.listPhysicians != null && response.listPhysicians.size()>0) {
                        // do your stuff to process response data
                    }

                    if (response.listQuestions != null && response.listQuestions.size()>0) {

                        // do your stuff to process response data
                    }


                } else {
                    // process error - show message
                }
            }, error -> {
                // process error - show network error message
            }));
}

答案 5 :(得分:1)

您将zip中的rxjavaJava 8一起使用:

Observable<MovieResponse> movies = ...
Observable<PictureResponse> picture = ...

Observable<ZipResponse> response = Observable.zip(movies, picture, ZipResponse::new);

class ZipResponse {
        private MovieResponse movieResponse;
        private PictureResponse pictureResponse;

        ZipResponse(MovieResponse movieResponse, PictureResponse pictureResponse) {
             this.movieResponse = movieResponse;
             this.pictureResponse = pictureResponse;
        }

        public MovieResponse getMovieResponse() {
             return movieResponse;
        }

        public void setMovieResponse(MovieResponse movieResponse) {
            this.movieResponse= movieResponse;
        }

        public PictureResponse getPictureResponse() {
             return pictureResponse;
        }

        public void setPictureResponse(PictureResponse pictureResponse) {
            this.pictureResponse= pictureResponse;
        }
}

答案 6 :(得分:0)

zip运算符允许您根据两个不同的observable的结果组合结果。

您必须提供lambda,它将根据每个observable发出的数据创建结果。

Observable<MovieResponse> movies = ...
Observable<PictureResponse> picture = ...

Observable<Response> response = movies.zipWith(picture, (movie, pic) -> {
        return new Response("description", movie.getName(), pic.getUrl());

});