Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to change parameters in retry request after error in RxJava

I send a login request to server with retrofit 2.0, and server return to the client session token, wich I must use in other requests, but this token has limited life-time, and when it is expire server returns HTTP error 401.

I try make re-logon, after getting this error, with help a next code:

    holder.getApi(GuideProfileApi.class)
      .getProfile(String.valueOf(holder.getServerId()), holder.getServerToken())
      .subscribeOn(Schedulers.io())
      .retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
         @Override
         public ObservableSource<?> apply(Observable<Throwable> throwableObservable) throws Exception {
           return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {
             @Override
             public ObservableSource<?> apply(Throwable throwable) throws Exception {
               if (throwable instanceof HttpException &&  ((HttpException)throwable).code() == 401) {
                 RegistryLoginResult loginResult = holder.login().blockingSingle();
                 return holder.getApi(GuideProfileApi.class)
                    .getProfile(String.valueOf(loginResult.getUserId()), loginResult.getSessionToken());
               }
               return Observable.error(throwable);
            }
        });
     }
  })
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe(new Consumer<ProfileResult>() {
    @Override
    public void accept(ProfileResult profileResult) throws Exception {
      Log.d("Result", profileResult.toString());
    }
  }, new Consumer<Throwable>() {
      @Override
      public void accept(Throwable throwable) throws Exception {
        Log.e("Result", throwable.getLocalizedMessage());
      }
});

And retry request is sent, but parameters of request are same as in incorrect request (before re-login). How I can change parameters of the request before sending it again?

like image 600
Pavel Avatar asked Jan 05 '23 17:01

Pavel


2 Answers

You can use retryWhen, but the problem is that your retryWhen retry the same observable object that you create in lazy moment. Your solution here is use the operator defer to get the host(), since defer it´s not creating the observable when you define it but when it´s consumed by the subscribed.

Observable.defer(()-> holder.getApi(GuideProfileApi.class)
          .getProfile(String.valueOf(holder.getServerId()),holder.getServerToken()))
  .subscribeOn(Schedulers.io())
  .retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
     @Override
     public ObservableSource<?> apply(Observable<Throwable> throwableObservable) throws Exception {
       return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {
         @Override
         public ObservableSource<?> apply(Throwable throwable) throws Exception {
           if (throwable instanceof HttpException &&  ((HttpException)throwable).code() == 401) {
             RegistryLoginResult loginResult = holder.login().blockingSingle();
             return holder.getApi(GuideProfileApi.class)
                .getProfile(String.valueOf(loginResult.getUserId()), loginResult.getSessionToken());
           }
           return Observable.error(throwable);
        }
    });
 }
  })
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe(new Consumer<ProfileResult>() {
    @Override
    public void accept(ProfileResult profileResult) throws Exception {
      Log.d("Result", profileResult.toString());
    }
      }, new Consumer<Throwable>() {
          @Override
          public void accept(Throwable throwable) throws Exception {
            Log.e("Result", throwable.getLocalizedMessage());
          }
    });

You can see some examples of retry here https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/errors/ObservableExceptions.java

like image 93
paul Avatar answered Jan 07 '23 07:01

paul


You're using the wrong operator. retryWhen will retry your original observable if it encounters an error. What you need is onErrorResumeNext. Something like

 holder.getApi(GuideProfileApi.class)
      .getProfile(String.valueOf(holder.getServerId()), holder.getServerToken())
      .subscribeOn(Schedulers.io())
      .onErrorResumeNext(new Function<Throwable, ObservableSource<?>>() {
        @Override
         public ObservableSource<?> apply(Throwable throwable) {
               if (throwable instanceof HttpException &&  ((HttpException)throwable).code() == 401) {
                 RegistryLoginResult loginResult = holder.login().blockingSingle();
                 return holder.getApi(GuideProfileApi.class)
                    .getProfile(String.valueOf(loginResult.getUserId()), loginResult.getSessionToken());
               }
               return Observable.error(throwable);
        }
    }) 
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe(new Consumer<ProfileResult>() {
    @Override
    public void accept(ProfileResult profileResult) throws Exception {
      Log.d("Result", profileResult.toString());
    }
  }, new Consumer<Throwable>() {
      @Override
      public void accept(Throwable throwable) throws Exception {
        Log.e("Result", throwable.getLocalizedMessage());
      }
});
like image 22
JohnWowUs Avatar answered Jan 07 '23 06:01

JohnWowUs