我知道标题听起来像是矛盾,但请听我的意见。
我在Android应用中使用刷新令牌和访问令牌身份验证。
在我的应用程序中,每个请求都通过post()
方法进入中央Network.Java类;
我在post()
方法中做的第一件事是检查我的令牌是否有效。如果是,则继续请求。
如果不是,我会调用refreshAccessToken()
函数。
现在在这个函数中,我使用refreshToken创建一个http请求来获取一个新的accessToken。但是我需要这个请求一次只执行一个实例。
用例,用户恢复应用。然后4个请求都被异步启动。然后他们每个人都看到了,我需要刷新我的令牌,然后他们就这样去了。(这会导致服务器端和本地问题)。我希望第一个执行令牌刷新,其他所有都要阻止(在工作线程上),直到第一个请求解锁它。
这是我的刷新功能(我尝试锁定): 对不起,这很长。
private static synchronized void refreshAccessToken(final Context context,final TokenResponseHandler callBack){
//Check if we have a valid refresh token
if (!refreshToken.isValid(context)) {
//There is no refresh token, so we have to force the user to login.
callBack.OnFailed(401, null, null, null);
//Start the login activity, this also clears all tokens
startLoginActivity(context);
//Exit as we can not do anything further
return;
}
//Refresh Token Should be valid here
//Make request to server for new access token
final RequestParams params = new RequestParams();
params.add("grant_type", "refresh_token");
params.add("refresh_token", refreshToken.getToken(context));
params.add("client_id", "android_app");
//Make the request
httpClient.post(BASE_URL + "Token", params, new AsyncHttpResponseHandler() {
@Override
public void onStart() {
//Lock this function down so that only one refresh happens at a time
tokenLock.lock();
//Check if the access token has not been refreshed by another thread since we been waiting
if (accessToken.isValid(context)) {
//tokenLock.unlock();
//TODO: end call some how
callBack.OnSuccess();
//Check if we need to manually call this.onFinnish(), it might be automatically called when we cancel the request
this.onFinish();
this.sendCancelMessage();
}
}
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
try {
if (HandleTokenResponse(context, responseBody)) {
callBack.OnSuccess();
//Check if we need to manually call this, it might be automatically called when we cancel it
this.onFinish();
return;
}
Log.e(TAG, "Failed to Obtained Access Token By Logging in with a refresh token");
callBack.OnFailed(500, null, null, new Throwable("Token Parse Error"));
} catch (Exception ex) {
Log.e(TAG, "Error occurred in token success response", ex);
}
}
@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
Log.e(TAG, "Failed to Obtained Access Token By Logging in with a refresh token");
if (responseBody != null) {
String response = new String(responseBody);
Log.e(TAG, response);
}
//notify parent that error occurred.
callBack.OnFailed(statusCode, headers, responseBody, error);
if (statusCode == 400 || statusCode == 401) {
//NB: We need to check that another thread / request has not already updated the access token.
if (!accessToken.isValid(context)) {
//Bad authentication then we need to login again
startLoginActivity(context);
} else {
//Continue as normal as the accessToken is ok, was probably updated by another entity
callBack.OnSuccess();
}
}
}
@Override
public void onFinish() {
tokenLock.unlock();
}
});
}
现在这是我可以阻止UI线程阻止它的唯一方法。我的synchronized
减速功能并没有帮助,因为所有功能都会触发同步任务。
但是我担心的是,onStart()
和onFinnish()
是由库在UI线程上执行的。我认为是这样的? (任何人都可以确认)
如何实现一个不会阻止我的UI线程的合适锁?