Android RxJava-等待retryWhen完成,以查看其他活动/片段中的其他可观察对象

Android RxJava-等待retryWhen完成,以查看其他活动/片段中的其他可观察对象,android,android-fragments,rx-java,reactive-programming,Android,Android Fragments,Rx Java,Reactive Programming,用例:我正在开发一个Android应用程序,它有一个带有4个选项卡的viewpager,所有这些选项卡都是片段。对于每个选项卡/片段,我必须每5分钟连接一个带有Oauth和令牌过期的RESTAPI 当前解决方案:使用RxJava和retryWhen操作符,当收到401 HTTP错误时,我可以重新进行身份验证。对于订阅和消费的每个可观察流,使用: retryWhen(refreshTokenAuthenticator) 因此,当令牌过期时,流将使用它,然后执行真正的api调用 问题:这只适用于一

用例:我正在开发一个Android应用程序,它有一个带有4个选项卡的viewpager,所有这些选项卡都是片段。对于每个选项卡/片段,我必须每5分钟连接一个带有Oauth和令牌过期的RESTAPI

当前解决方案:使用RxJava和retryWhen操作符,当收到401 HTTP错误时,我可以重新进行身份验证。对于订阅和消费的每个可观察流,使用:

retryWhen(refreshTokenAuthenticator)
因此,当令牌过期时,流将使用它,然后执行真正的api调用

问题:这只适用于一个订阅中消耗的一个可观察项,但我需要允许用户在选项卡之间切换,而不会阻止他/她,因为401错误可能随时出现在任何Api调用的任何片段中

问题:有没有办法让观察对象等待onNext()完成的其他观察对象,它们不在同一个流/订阅服务器中?事实上,在不同的片段?因此,api调用场景如下所示:

Api Call Fragment A --> request
Api Call Fragment A <-- response 200 Code

Api Call Fragment B --> request
Api Call Fragment B <-- response 401 Code (retryWhen in action)
Api Call Fragment B --> request (refreshToken)
Api Call Fragment B <-- response 200 (with new access token saved in the app)

Api调用片段C-->请求
Api调用片段C调用(可观察>(){
int retryCount=0;
@凌驾
公共可观察呼叫(最终可丢弃){
retryCount++;
if(retryCount sessionManager.saveAuthToken(tokenDto))
.doon错误(可丢弃1->{
Log.e(“RefreshTokenAuth”,“DoOnError”,throwable1);
application.logout();
});
}
}
//不再重试。请传递原始改装错误。
返回可观测误差(可丢弃);
}
});
}
}

1)使身份验证令牌源缓存上次成功的结果+提供使此缓存结果无效的方法:

class Auth {
    private Observable<AuthToken> validToken;

    synchronized void invalidateAuthToken() {
        validToken = null;
    }

    synchronized Observable<AuthToken> getAuthToken() {
        if (validToken == null) {
            validToken = repository
                .refreshToken(...) // start async request
                .doOnError(e -> invalidateAuthToken())
                .replay(1); // cache result
        }
        return validToken; // share among all subscribers
    }
}
类身份验证{ 私人可观察有效性; 同步的void invalidateAuthToken()无效{ validToken=null; } 同步可观测getAuthToken(){ if(validToken==null){ validToken=存储库 .refreshToken(…)//启动异步请求 .doError(e->invalidateAuthToken()) .replay(1);//缓存结果 } return validToken;//在所有订阅者之间共享 } } 2) 要访问web服务,请使用以下模式:

Observable<Data1> dataSource1 = 
    Observable.defer(auth.getAuthToken()) // always start from token
        .flatMap(token ->
            repository.fetchData1(token, ...)) // use token to call web service
        .doOnError(e -> auth.invalidateAuthToken())
        .retry(N); // retry N times
可观测数据源1=
Observable.defer(auth.getAuthToken())//始终从标记开始
.flatMap(令牌->
repository.fetchData1(token,…)//使用token调用web服务
.doError(e->auth.invalidateAuthToken())
.重试(N);//重试N次

如果应用程序当前是否正在重新验证,只需添加一个全局(在我的应用程序类中)布尔值,就可以让它工作。它实际上允许两个401HTTP错误,但第二个错误在onNext()中继续,并重新执行初始可观察的。我想做一些反应性更强的事情,但至少这解决了我的主要问题

public class RefreshTokenAuthenticator implements Func1<Observable<? extends Throwable>, Observable<?>> {

private static final int RETRY_COUNT = 1;

private static final int HTTP_ERROR_CODE = 401;

@Inject
private UserRepository repository;

@Inject
private SessionManager sessionManager;

@Inject
private MyApplication application;


@Inject
private RefreshTokenAuthenticator() {
}

@Override
public Observable<?> call(Observable<? extends Throwable> observable) {
    return observable
            .flatMap(new Func1<Throwable, Observable<?>>() {
                int retryCount = 0;

                @Override
                public Observable<?> call(final Throwable throwable) {

                    retryCount++;
                    if (retryCount <= RETRY_COUNT && throwable instanceof HttpException) {
                        int errorCode = ((HttpException) throwable).code();

                        if (errorCode == HTTP_ERROR_CODE) {

                            Log.i("RefreshTokenAuth", "APPLICATION IS AUTHENTICATING = " + application.isAuthenticating);
                            if (!application.isAuthenticating) {
                                application.isAuthenticating = true;

                                String refreshToken = sessionManager.getAuthToken().getRefreshToken();

                                return repository
                                        .refreshToken(refreshToken)
                                        .observeOn(AndroidSchedulers.mainThread())
                                        .subscribeOn(Schedulers.io())
                                        .doOnCompleted(() -> application.isAuthenticating = false)
                                        .doOnNext(tokenDto -> sessionManager.saveAuthToken(tokenDto))
                                        .doOnError(throwable1 -> {
                                            Log.e("RefreshTokenAuth", "DoOnError", throwable1);
                                            application.logout();
                                        });
                            } else {
                                return Observable.just(1).doOnNext(o -> Log.i("RefreshTokenAuth", "Let's try another shot!"));
                            }
                        }
                    }
                    // No more retries. Pass the original Retrofit error through.
                    return Observable.error(throwable);
                }
            });
}
公共类RefreshTokenAuthenticator实现Func1>{
私有静态最终整数重试计数=1;
私有静态final int HTTP_ERROR_CODE=401;
@注入
私有用户存储库;
@注入
私有会话管理器会话管理器;
@注入
私人MyApplication;
@注入
私有刷新令牌身份验证器(){
}
@凌驾
公共可观察呼叫(可观察>(){
int retryCount=0;
@凌驾
公共可观察呼叫(最终可丢弃){
retryCount++;
if(retryCount application.IsAuthentication=false)
.doOnNext(tokenDto->sessionManager.saveAuthToken(tokenDto))
.doon错误(可丢弃1->{
Log.e(“RefreshTokenAuth”,“DoOnError”,throwable1);
application.logout();
});
}否则{
返回Observable.just(1).doOnNext(o->Log.i(“RefreshTokenAuth”,“让我们再试一次吧!”);
}
}
}
//不再重试。请传递原始改装错误。
返回可观测误差(可丢弃);
}
});
}

}

我正在使用and拦截器和改型2截取请求,并在头上附加令牌。你认为你的答案(以Rx方式使用authtoken)更好还是至少更容易处理?@NicolasJafelle我的例子是通用的。您可以实现
fetchData1
函数,以便它以改进的方式注入令牌。
public void getDashboardDetail() {

    Subscription subscription = repository.getDashboard()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .retryWhen(tokenAuthenticator)
            .subscribe(new RestHttpObserver<UserDataDto>() {
                @Override
                public void onUnknownError(Throwable e) {
                    getMvpView().onError(e);
                }

                @Override
                public void onHostUnreachable() {
                    getMvpView().onHostUnreachable();
                }

                @Override
                public void onHttpErrorCode(int errorCode, ErrorDto errorDto) {
                    getMvpView().onHttpErrorCode(errorCode, errorDto);
                }

                @Override
                public void onCompleted() {
                    //Do nothing...
                }

                @Override
                public void onNext(UserDataDto response) {
                    getMvpView().onReceiveUserData(response);
                }
            });

    this.compositeSubscription.add(subscription);

}
public class RefreshTokenAuthenticator implements Func1<Observable<? extends Throwable>, Observable<?>> {

private static final int RETRY_COUNT = 1;

private static final int HTTP_ERROR_CODE = 401;

@Inject
private UserRepository repository;

@Inject
private SessionManager sessionManager;

@Inject
private MyApplication application;


@Inject
private RefreshTokenAuthenticator() {
}

@Override
public synchronized Observable<?> call(Observable<? extends Throwable> observable) {
    return observable
            .flatMap(new Func1<Throwable, Observable<?>>() {
                int retryCount = 0;

                @Override
                public Observable<?> call(final Throwable throwable) {

                    retryCount++;
                    if (retryCount <= RETRY_COUNT && throwable instanceof HttpException) {
                        int errorCode = ((HttpException) throwable).code();
                        if (errorCode == HTTP_ERROR_CODE) {
                            return repository
                                    .refreshToken(sessionManager.getAuthToken().getRefreshToken())
                                    .observeOn(AndroidSchedulers.mainThread())
                                    .subscribeOn(Schedulers.io())

                                    .doOnNext(tokenDto -> sessionManager.saveAuthToken(tokenDto))
                                    .doOnError(throwable1 -> {
                                        Log.e("RefreshTokenAuth", "DoOnError", throwable1);
                                        application.logout();
                                    });

                        }
                    }
                    // No more retries. Pass the original Retrofit error through.
                    return Observable.error(throwable);
                }
            });
}
class Auth {
    private Observable<AuthToken> validToken;

    synchronized void invalidateAuthToken() {
        validToken = null;
    }

    synchronized Observable<AuthToken> getAuthToken() {
        if (validToken == null) {
            validToken = repository
                .refreshToken(...) // start async request
                .doOnError(e -> invalidateAuthToken())
                .replay(1); // cache result
        }
        return validToken; // share among all subscribers
    }
}
Observable<Data1> dataSource1 = 
    Observable.defer(auth.getAuthToken()) // always start from token
        .flatMap(token ->
            repository.fetchData1(token, ...)) // use token to call web service
        .doOnError(e -> auth.invalidateAuthToken())
        .retry(N); // retry N times
public class RefreshTokenAuthenticator implements Func1<Observable<? extends Throwable>, Observable<?>> {

private static final int RETRY_COUNT = 1;

private static final int HTTP_ERROR_CODE = 401;

@Inject
private UserRepository repository;

@Inject
private SessionManager sessionManager;

@Inject
private MyApplication application;


@Inject
private RefreshTokenAuthenticator() {
}

@Override
public Observable<?> call(Observable<? extends Throwable> observable) {
    return observable
            .flatMap(new Func1<Throwable, Observable<?>>() {
                int retryCount = 0;

                @Override
                public Observable<?> call(final Throwable throwable) {

                    retryCount++;
                    if (retryCount <= RETRY_COUNT && throwable instanceof HttpException) {
                        int errorCode = ((HttpException) throwable).code();

                        if (errorCode == HTTP_ERROR_CODE) {

                            Log.i("RefreshTokenAuth", "APPLICATION IS AUTHENTICATING = " + application.isAuthenticating);
                            if (!application.isAuthenticating) {
                                application.isAuthenticating = true;

                                String refreshToken = sessionManager.getAuthToken().getRefreshToken();

                                return repository
                                        .refreshToken(refreshToken)
                                        .observeOn(AndroidSchedulers.mainThread())
                                        .subscribeOn(Schedulers.io())
                                        .doOnCompleted(() -> application.isAuthenticating = false)
                                        .doOnNext(tokenDto -> sessionManager.saveAuthToken(tokenDto))
                                        .doOnError(throwable1 -> {
                                            Log.e("RefreshTokenAuth", "DoOnError", throwable1);
                                            application.logout();
                                        });
                            } else {
                                return Observable.just(1).doOnNext(o -> Log.i("RefreshTokenAuth", "Let's try another shot!"));
                            }
                        }
                    }
                    // No more retries. Pass the original Retrofit error through.
                    return Observable.error(throwable);
                }
            });
}