Rx java 改装2+;RxJava2,无效令牌,当retryWhen()重新订阅时如何更新流

Rx java 改装2+;RxJava2,无效令牌,当retryWhen()重新订阅时如何更新流,rx-java,retrofit2,rx-java2,reactive,resque-retry,Rx Java,Retrofit2,Rx Java2,Reactive,Resque Retry,下面我有一个简单的代码,它模拟了我目前试图完成的一个场景 mApiService.api().postSomethingWithAccessToken(request, "some_invalid_access_token") .subscribeOn(Schedulers.io()) .retryWhen(new Function<Observable<Throwable>, ObservableSource<Access

下面我有一个简单的代码,它模拟了我目前试图完成的一个场景

mApiService.api().postSomethingWithAccessToken(request, "some_invalid_access_token")
            .subscribeOn(Schedulers.io())
            .retryWhen(new Function<Observable<Throwable>, ObservableSource<AccessToken>>() {

                @Override
                public ObservableSource<AccessToken> apply(Observable<Throwable> throwableObservable) throws Exception {
                    return mApiService.api().getAccessToken();
                }
            })
            .subscribeOn(Schedulers.io())
            .subscribe(new Observer<Void>() {
                @Override
                public void onSubscribe(Disposable d) {
                }

                @Override
                public void onNext(Void value) {
                }

                @Override
                public void onError(Throwable e) {

                    e.printStackTrace();
                    onError(e);
                }

                @Override
                public void onComplete() {
                }
            });
我注意到(我放了一个日志)外部observable的subscribe和retryWhen是在主线程上执行的,但是重试/重新订阅的流跳过了不同调度器的线程,这似乎是一个竞争条件:(


这里有几个问题:

  • 重试时,您需要将访问令牌传递回
    postSomethingWithAccessToken
    方法,否则您将使用相同的旧无效访问令牌重试
  • 当逻辑不正确时,您必须响应您得到的错误
    Observable
    ,并将重试逻辑放在那里。正如您所说,此方法首先执行,而不是在错误发生时执行,
    throwableObservable
    是对错误的响应,它将错误镜像为发射(
    onNext()
    ),您可以
    flatMap()
    完成每个错误和响应,或者使用error(用于将错误传递到源流)完成,或者使用
    onNext()
    和某个对象发出重试信号。
    在这个问题上有一个很好的答案
所以您需要:
1) 将访问令牌存储在您可以使用访问令牌刷新进行更改的位置。
2) 修复重试逻辑以正确响应错误

以下是建议代码:

postSomethingWithAccessToken(request, accessToken)
        .subscribeOn(Schedulers.io())
        .retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
                   @Override
                   public ObservableSource<?> apply(
                           @NonNull Observable<Throwable> throwableObservable) throws Exception {
                       return throwableObservable.flatMap(
                               new Function<Throwable, ObservableSource<? extends R>>() {
                                   @Override
                                   public ObservableSource<? extends R> apply(
                                           @NonNull Throwable throwable) throws Exception {
                                       if (throwable.code == 401) { //or 404/403, just a pseudo-code, put your real error comparing logic here
                                           return getAccessToken()
                                                           .doOnNext(refreshedToken -> accessToken.updateToken(refreshedToken));
                                                   //or keep accessToken on some field, the point to have mutable
                                                   //var that you can change and postSomethingWithAccessToken can see
                                       }
                                       return Observable.error(throwable);
                                   }
                               });
                       }
                   }
        )
        .subscribeOn(Schedulers.io())
        .subscribe(new Consumer<Result>() {
                       @Override
                       public void accept(@NonNull Result result) throws Exception {
                           //handle result
                       }
                   }
        );
postSomethingWithAccessToken(请求,accessToken)
.subscribeOn(Schedulers.io())
.retryWhen(新功能适用)(
@非Null Observable throwableObservable)引发异常{
返回可丢弃可观察的.flatMap(

新功能非常感谢yosriz,因为他为我指出了解决磨牙问题的正确方向,我必须使用
延迟
。因此我在GitHub解决了这个问题

这与我现在遇到的问题完全相同,对于任何遇到相同问题的人,这里是我的解决方案

Observable
    .defer(new Callable<ObservableSource<?>>() {
        @Override
        public ObservableSource<?> call() throws Exception {
            // return an observable source here, the observable that will be the source of the entire stream;
        }
    })
    .subscribeOn( /*target thread to run*/ )
    .retryWhen( {
        // return a throwable observable here that will perform the logic when an error occurred
    })
    .subscribe( /*subscription here*/ )
可观察
.defer(new Callable call()引发异常{
//在这里返回一个可观测源,该可观测源将是整个流的源;
}
})
.subscribeOn(/*要运行的目标线程*/)
.retryWhen({
//在此处返回一个可丢弃的可观察值,当发生错误时,该值将执行逻辑
})
.订阅(/*此处订阅*/)
或者这是我的解决方案的完整非lambda

Observable
    .defer(new Callable<ObservableSource<?>>() {
        @Override
        public ObservableSource<?> call() throws Exception {
            return mApiService.api().postSomethingWithAccessToken(
                request, preferences.getString("access_token", ""));
        }
    })
    .subscribeOn(Schedulers.io())
    .retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
        @Override
        public ObservableSource<?> apply(final 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 httpException = (HttpException) throwable;
                        if (httpException.code() == 401) {
                            return mApiService.api().getAccessToken().doOnNext(new Consumer<Authentication>() {
                                    @Override
                                    public void accept(Authentication authentication) throws Exception {
                                        update(authentication);
                                    }
                                });
                        }
                    }
                    return Observable.error(throwable);
                }
            });
        }
    })
    .subscribe(new Observer<Void>() {
        @Override
        public void onSubscribe(Disposable d) {
            Log.e("subscribe", "TOKEN : " + preferences.getString("access_token", ""));
        }

        @Override
        public void onNext(Void value) {
            Log.e("onNext", "TOKEN : " + preferences.getString("access_token", ""));
        }

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

        @Override
        public void onComplete() {
            Log.e("Complete", "____ COMPLETE");
        }
    });
可观察
.defer(new Callable call()引发异常{
返回mApiService.api().postSomethingWithAccessToken(
请求,preferences.getString(“access_token”,”);
}
})
.subscribeOn(Schedulers.io())
.retryWhen(新函数apply(最终可观测throwableObservable)引发异常{
return throwableObservable.flatMap(新函数apply(Throwable Throwable))抛出异常{
if(HttpException的可丢弃实例){
HttpException HttpException=(HttpException)可丢弃;
if(httpException.code()=401){
返回mApiService.api().getAccessToken().doOnNext(新使用者(){
@凌驾
public void accept(身份验证)引发异常{
更新(认证);
}
});
}
}
返回可观测误差(可丢弃);
}
});
}
})
.订阅(新观察员){
@凌驾
认购的公共无效(一次性d){
Log.e(“subscribe”,“TOKEN:”+preferences.getString(“access_TOKEN)”,“”);
}
@凌驾
公共void onNext(void值){
Log.e(“onNext”,“TOKEN:+preferences.getString”(“access_TOKEN”),TOKEN:“);
}
@凌驾
公共无效申报人(可丢弃的e){
e、 printStackTrace();
}
@凌驾
未完成的公共空间(){
Log.e(“完成”、“完成”);
}
});

这里的关键点是“如何在
.retryWhen()
操作员重新订阅源可观察对象时修改/更新现有源可观察对象”

您好,非常感谢您提供了一个代码示例并提到了错误点,如果问题不多的话,您介意将代码更改为非lambda吗?非常感谢!我将开始,我将尝试重新编写代码。:),也感谢链接:)只是一个问题,首先执行retryWhen()是否真的正常?在实际外部可观察执行之前?-我不知道我是否理解正确->“订阅时会调用工厂Func1来设置重试逻辑。这样,当调用OneError时,您已经定义了如何处理它。”是的,正如前面所解释的,重试逻辑首先由Func1方法构造,然后throwableObservable将订阅,只有源Observable才会订阅。这就是为什么您不能在那里执行实际工作,您应该将您的重试逻辑集成到throwableObservable流中。这意味着重试是基于对throwableObservable错误发射的反应,而不是Func1方法体。您建议的代码完全正常工作,我只是在更新令牌时遇到了一些问题,即使是作为静态变量或对象字段使重试不停,“重试逻辑首先由Func1方法构造,然后throwableObservable将订阅,只有SourceObservable才会订阅。”-谢谢,这对我来说是一个很好的开端:):)谢谢谢谢
postSomethingWithAccessToken(request, accessToken)
        .subscribeOn(Schedulers.io())
        .retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
                   @Override
                   public ObservableSource<?> apply(
                           @NonNull Observable<Throwable> throwableObservable) throws Exception {
                       return throwableObservable.flatMap(
                               new Function<Throwable, ObservableSource<? extends R>>() {
                                   @Override
                                   public ObservableSource<? extends R> apply(
                                           @NonNull Throwable throwable) throws Exception {
                                       if (throwable.code == 401) { //or 404/403, just a pseudo-code, put your real error comparing logic here
                                           return getAccessToken()
                                                           .doOnNext(refreshedToken -> accessToken.updateToken(refreshedToken));
                                                   //or keep accessToken on some field, the point to have mutable
                                                   //var that you can change and postSomethingWithAccessToken can see
                                       }
                                       return Observable.error(throwable);
                                   }
                               });
                       }
                   }
        )
        .subscribeOn(Schedulers.io())
        .subscribe(new Consumer<Result>() {
                       @Override
                       public void accept(@NonNull Result result) throws Exception {
                           //handle result
                       }
                   }
        );
Observable
    .defer(new Callable<ObservableSource<?>>() {
        @Override
        public ObservableSource<?> call() throws Exception {
            // return an observable source here, the observable that will be the source of the entire stream;
        }
    })
    .subscribeOn( /*target thread to run*/ )
    .retryWhen( {
        // return a throwable observable here that will perform the logic when an error occurred
    })
    .subscribe( /*subscription here*/ )
Observable
    .defer(new Callable<ObservableSource<?>>() {
        @Override
        public ObservableSource<?> call() throws Exception {
            return mApiService.api().postSomethingWithAccessToken(
                request, preferences.getString("access_token", ""));
        }
    })
    .subscribeOn(Schedulers.io())
    .retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
        @Override
        public ObservableSource<?> apply(final 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 httpException = (HttpException) throwable;
                        if (httpException.code() == 401) {
                            return mApiService.api().getAccessToken().doOnNext(new Consumer<Authentication>() {
                                    @Override
                                    public void accept(Authentication authentication) throws Exception {
                                        update(authentication);
                                    }
                                });
                        }
                    }
                    return Observable.error(throwable);
                }
            });
        }
    })
    .subscribe(new Observer<Void>() {
        @Override
        public void onSubscribe(Disposable d) {
            Log.e("subscribe", "TOKEN : " + preferences.getString("access_token", ""));
        }

        @Override
        public void onNext(Void value) {
            Log.e("onNext", "TOKEN : " + preferences.getString("access_token", ""));
        }

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

        @Override
        public void onComplete() {
            Log.e("Complete", "____ COMPLETE");
        }
    });