5

我有一个使用 OAuth2 进行身份验证并使用 Retrofit 从 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(...);
}

这可行,但在某些情况下,一次发送多个请求并且它们都调用刷新过程 - 基本上我的应用程序最终刷新令牌的次数与当前请求的次数一样多。

所以,问题是:我如何确保当令牌需要刷新时,它只执行一次,不管有多少正在进行的请求需要刷新令牌?我能否以某种方式让其他请求“等待”直到第一个请求成功调用并检索到新令牌?

4

1 回答 1

3

我们已经使用 hot observable 来刷新令牌并为所有未能通过身份验证的请求提供对其实例的访问来完成此行为。

使用shareoperator 将用于刷新令牌的基本冷 observable 转换为热令牌,以便其他所有订阅者共享其结果。一旦请求返回,所有等待的观察者都会收到通知,并且在那一刻(在操作员链中,它恰好在share()回调 for之前doOnUnsubscribe)销毁刷新的可观察实例,以便下一个订阅者创建新实例。所有这些都可以通过单例模式轻松实现,您可以将刷新的 observable 包装到单例包装类中,然后通过 getInstance() 请求它。如果没有请求进行——实例为空——getInstance 应该创建一个新的。

您还需要注意其他一些事情,例如刷新期间的错误和使令牌无效,但这些是基础知识。

我现在没有太多时间来详细说明这一点,但是如果您在自己实现它时遇到一些麻烦,请发表评论,我将在明天发布一些代码示例。如果没有上下文,它们就没有多大意义。

于 2017-01-24T15:12:33.873 回答