Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RxJava - check condition and repeat once only if condition is true

I use RxJava + Retrofit to make API calls in my Android app. There may be cases when user makes a request and his token is expired. In this cases I receive a normal response in my onNext, but the response contains not the result but an error element with some code. If such thing happens I need to re-login the user and only after getting a new token repeat the original request. So I want to organize this using RxJava. To make things easier I will bring a simple example. Let's say I have the following method:

public void test(int someInt){
    Observable.just(someInt)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Subscriber<Integer>() {
                @Override
                public void onCompleted() {
                    log("onCompleted");
                }

                @Override
                public void onError(Throwable e) {
                    e.printStackTrace();
                    log("onError");
                }

                @Override
                public void onNext(Integer integer) {
                    log("onNext - " + integer);
                }
            });

I want to check if (someInt == 0) before onNext() is called. If I get false I want to continue and get onNext() called, but if I get true I want to perform some action and repeat the original observable only once, if the condition returns false second time I don't want to repeat again.

Can someone help me to figure out what options do I have for this? P.S. I am new in RX world.

like image 727
Andranik Avatar asked Feb 26 '16 11:02

Andranik


1 Answers

Here you go. Since you want to retry the whole chain .retryWhen is great for it so you have to "play" a bit with the errors.

Below if you detect a invalid token, you pass an error (only on the first time) which the retryWhen will catch and resubscribe to the whole rx chain (starting from Observable.just(someInt)).

 haveRetriedOnce = false;

 Observable.just(someInt)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .flatMap(integer ->{
          if(integer == 0){
            if(haveRetriedOnce){
               return Observable.error(new UserOperationException());
            }
            // problem, throw an error and the .retryWhen will catch it
            return Observable.error(new InvalidTokenException());
          }else{
            return Observable.just(integer);
          }
        })
        .retryWhen(observable -> observable.flatMap(throwable->{
          if(throwable instanceOf InvalidTokenException){
            haveRetriedOnce = true;
            return just(0); // retry, the int here is irrelevant
          }else{
            // other error, pass it further
            return Observable.error(throwable);
          }
        }))
        .subscribe(new Subscriber<Integer>() {
            @Override
            public void onCompleted() {
                log("onCompleted");
            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
                log("onError");
            }

            @Override
            public void onNext(Integer integer) {
                log("onNext - " + integer);
            }
        }
like image 178
Diolor Avatar answered Oct 17 '22 03:10

Diolor