How to combine multiple RxJava chains non-blocking in error case

后端 未结 5 1456
旧巷少年郎
旧巷少年郎 2020-12-15 10:19

My requirements:

  • N Retrofit calls in parallel
  • Wait for all calls to finish (success or failure)
  • If k (0<= k < N) calls fail, they sho
5条回答
  •  醉酒成梦
    2020-12-15 11:08

    1. You can not achieve parallel via combineLast or zip, rxjava will execute and emit your items in sequence in my testing.

    2. If one of your task fail, your Func2#call will not get called and onError will submitted instead. You even can not get the results of other successful tasks in this way.

    3. The solution is flatMap, it's the traditional way to achieve concurrent in rxjava. It also meet your other requirements.

    Here is a small but completed example.

    I use a simple website service to test.

    I use a Semaphore to wait for all task done, you can completely ignore it. And I add logging to the http request for better understanding, you can complete ignore it also.

    public interface WebsiteService {
    
        @GET
        Observable website(@Url String url);
    
    }
    

    Then I use the following to test the result with rxjava.

       HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
    
        Retrofit retrofit = new Retrofit.Builder().baseUrl("https://www.google.com")
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(new OkHttpClient.Builder().addInterceptor(loggingInterceptor).build())
                .build();
        WebsiteService websiteService = retrofit.create(WebsiteService.class);
    
        final Semaphore s = new Semaphore(1);
        try {
            s.acquire();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    
        Observable first = websiteService.website("http://github.com");
        Observable second = websiteService.website("http://stackoverflow.com");
        Observable third = websiteService.website("http://notexisting.com");
    
        final int numberOfCalls = 3; // testing for three calls
    
        Observable.just(first, second, third)
                .flatMap(new Function, ObservableSource>() {
                    @Override
                    public ObservableSource apply(@NonNull Observable responseBodyObservable) throws Exception {
                        return responseBodyObservable.subscribeOn(Schedulers.computation());
                    }
                })
                .subscribeOn(Schedulers.computation())
                .subscribe(new Observer() {
    
                    private int currentDoneCalls = 0;
    
                    private void checkShouldReleaseSemaphore() {
                        if (currentDoneCalls >= numberOfCalls) {
                            s.release();
                        }
                    }
    
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
    
                    }
    
                    @Override
                    public void onNext(@NonNull ResponseBody responseBody) {
                        System.out.println("Retrofit call success " + responseBody.contentType());
                        synchronized (this) {
                            currentDoneCalls++;
                        }
                        checkShouldReleaseSemaphore();
                    }
    
                    @Override
                    public void onError(@NonNull Throwable e) {
                        System.out.println("Retrofit call failed " + e.getMessage());
                        synchronized (this) {
                            currentDoneCalls++;
                        }
                        checkShouldReleaseSemaphore();
                    }
    
                    @Override
                    public void onComplete() {
                        System.out.println("onComplete, All request success");
                        checkShouldReleaseSemaphore();
                    }
    
                });
    
        try {
            s.acquire();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("All request done");
            s.release();
        }
    

    I use rxjava2 and retrofit adapter-rxjava2 for testing.

    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
    compile 'io.reactivex.rxjava2:rxjava:2.1.0'
    compile 'com.squareup.retrofit2:retrofit:2.3.0'
    compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
    compile 'com.squareup.okhttp3:logging-interceptor:3.8.1'
    

    Updated

    The introduction page of RxJava2 from github has pointed out the practical way to implement paralellism.

    Practically, paralellism in RxJava means running independent flows and merging their results back into a single flow. The operator flatMap does this...

    Although this example is based on RxJava2, the operation flatMap is already existing in RxJava.

提交回复
热议问题