通过拦截器刷新服务器令牌时 OkHttp 请求泄漏
OkHttp requests are leaking when refreshing server tokens through interceptors
我使用 RxJava 和 Retrofit 来执行 OkHttp 请求。我可以在 Android Studio 的网络分析器中看到我的请求已泄露,因为它们使 AsyncTask 线程保持活动状态。他们没有回应,他们的大小为空。我可以在网络分析器的线程视图中看到原始请求、令牌请求和更新后的请求。但是最初的请求永远不会完成。
在极端情况下,例如在 QA 环境中,令牌由于某种原因每分钟刷新一次,线程池已满,无法再进行调用。我认为我们可以根据需要多次调用 chain.proceed,但我认为错误仍然存在。这是拦截器代码:
private static okhttp3.Interceptor oauthInterceptor = chain -> {
OAuthToken token = OAuthToken.getOAuthToken();
long initialTokenCreated = token.getCreatedUtc();
Request request = changeTokenInRequest(chain.request(), token);
Response response = chain.proceed(request);
boolean forceTokenRefresh = false;
if (initialTokenCreated != token.getCreatedUtc()) {
// Then the token has been updated since we started this request
request = changeTokenInRequest(chain.request(), token);
response = chain.proceed(request);
}
String jsonType = "application/json";
if (!response.body().contentType().toString().contains(jsonType)) {
forceTokenRefresh = true;
}
// 401: Forbidden, 403: Permission denied
if (forceTokenRefresh || ((response.code() == 401 || response.code() == 403)
&& OAuthToken.getOAuthToken().getRefreshToken() != null)) {
OAuthToken refreshedToken = refreshToken();
if (refreshedToken == null) {
// Then there was a problem refreshing the token
return response;
}
// Recreate the request with the new access token
request = changeTokenInRequest(chain.request(), refreshedToken);
return chain.proceed(request);
} else {
return response;
}
};
protected static okhttp3.Request changeTokenInRequest(Request request, OAuthTokenBase token) {
Headers.Builder builder = request.headers().newBuilder().removeAll("Authorization");
if (!TextUtils.isEmpty(token.getTokenType()) && !TextUtils.isEmpty(token.getAccessToken())) {
builder = builder.add("Authorization", token.getTokenType() + " " + token.getAccessToken());
}
request = request.newBuilder().headers(builder.build()).build();
return request;
}
我就是这样打电话的:
public static Observable<ResultPaginatedReply<BasicUser>> getGroupsMembers(String type, String id, int page) {
return getMsApiService().getGroupsMembers(type, id, page)
.subscribeOn(Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR))
.observeOn(AndroidSchedulers.mainThread());
}
事实证明,如果你像我一样不使用 OkHttp 中的 Authenticator class,那么你必须在刷新令牌时手动关闭响应主体。 @DallinDyer 的评论对我有帮助 here。
所以我添加了
response.body().close();
在错误检查之后,瞧瞧。
我使用 RxJava 和 Retrofit 来执行 OkHttp 请求。我可以在 Android Studio 的网络分析器中看到我的请求已泄露,因为它们使 AsyncTask 线程保持活动状态。他们没有回应,他们的大小为空。我可以在网络分析器的线程视图中看到原始请求、令牌请求和更新后的请求。但是最初的请求永远不会完成。
在极端情况下,例如在 QA 环境中,令牌由于某种原因每分钟刷新一次,线程池已满,无法再进行调用。我认为我们可以根据需要多次调用 chain.proceed,但我认为错误仍然存在。这是拦截器代码:
private static okhttp3.Interceptor oauthInterceptor = chain -> {
OAuthToken token = OAuthToken.getOAuthToken();
long initialTokenCreated = token.getCreatedUtc();
Request request = changeTokenInRequest(chain.request(), token);
Response response = chain.proceed(request);
boolean forceTokenRefresh = false;
if (initialTokenCreated != token.getCreatedUtc()) {
// Then the token has been updated since we started this request
request = changeTokenInRequest(chain.request(), token);
response = chain.proceed(request);
}
String jsonType = "application/json";
if (!response.body().contentType().toString().contains(jsonType)) {
forceTokenRefresh = true;
}
// 401: Forbidden, 403: Permission denied
if (forceTokenRefresh || ((response.code() == 401 || response.code() == 403)
&& OAuthToken.getOAuthToken().getRefreshToken() != null)) {
OAuthToken refreshedToken = refreshToken();
if (refreshedToken == null) {
// Then there was a problem refreshing the token
return response;
}
// Recreate the request with the new access token
request = changeTokenInRequest(chain.request(), refreshedToken);
return chain.proceed(request);
} else {
return response;
}
};
protected static okhttp3.Request changeTokenInRequest(Request request, OAuthTokenBase token) {
Headers.Builder builder = request.headers().newBuilder().removeAll("Authorization");
if (!TextUtils.isEmpty(token.getTokenType()) && !TextUtils.isEmpty(token.getAccessToken())) {
builder = builder.add("Authorization", token.getTokenType() + " " + token.getAccessToken());
}
request = request.newBuilder().headers(builder.build()).build();
return request;
}
我就是这样打电话的:
public static Observable<ResultPaginatedReply<BasicUser>> getGroupsMembers(String type, String id, int page) {
return getMsApiService().getGroupsMembers(type, id, page)
.subscribeOn(Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR))
.observeOn(AndroidSchedulers.mainThread());
}
事实证明,如果你像我一样不使用 OkHttp 中的 Authenticator class,那么你必须在刷新令牌时手动关闭响应主体。 @DallinDyer 的评论对我有帮助 here。
所以我添加了
response.body().close();
在错误检查之后,瞧瞧。