我有一个应用程序,它使用OAuth2进行身份验证,并使用Reform从RESTful服务中获取数据。现在,我有了令牌检索和刷新并运行。令牌的刷新方式如下(省略了调度器):
// Each Retrofit call observable is "wrapper" using this method
protected <T> Observable<T> wrap(@NonNull final Observable<T> page) {
return authenticate()
.concatMap(token -> page)
.onErrorResumeNext(throwable -> {
Log.w(TAG, "wrap: ErrorResumeNext", throwable);
return refreshAccessToken()
.flatMap(accessToken -> page);
}));
}
// Retrieves the access token if necessary
Observable<AccessToken> authenticate() {
// Already have token
if(accessToken != null) return Observable.just(accessToken);
// No token yet, fetch it
return api.getAccessToken(...);
}
// Refreshes the token
Observable<AccessToken> refreshAccessToken() {
return api.refreshToken(...);
}
这是可行的,但在某些情况下,同时发送多个请求,它们都会调用刷新过程——基本上,我的应用程序最终会刷新令牌的次数与当前请求的次数一样多。
因此,问题是:无论有多少个正在进行的请求需要刷新令牌,我如何确保在需要刷新令牌时只进行一次?我能以某种方式让其他请求"等待",直到第一个请求成功调用并检索到新令牌吗?
我们已经使用热可观察来刷新令牌并为所有未能进行身份验证的请求提供对其实例的访问,从而完成了此行为。
使用share
运算符将用于刷新令牌的基本冷可观测值转换为热可观测值,以便每个其他订阅者共享其结果。一旦请求返回,所有等待的观察者都会收到通知,并在那一刻(在运营商链中,它正好在share()
之前进入doOnUnsubscribe
的回调)销毁刷新的可观察实例,以便下一个订阅者创建新实例。所有这些都可以通过singleton模式轻松实现,在该模式中,您可以将刷新的observable封装到singleton包装器类中,并通过getInstance()请求它。如果没有正在进行的请求——实例为null——getInstance应该创建一个新的请求。
您还需要处理其他一些事情,例如刷新过程中的错误和使令牌无效,但这些都是最基本的。
我现在没有太多时间来详细说明这一点,但如果您在自己实现这一点时遇到一些困难,请留下评论,我将在明天发布一些代码示例。如果没有上下文,它们就没有多大意义。