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()
完成每个错误和响应,或者使用error(用于将错误传递到源流)完成,或者使用flatMap()
和某个对象发出重试信号。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");
}
});