AccountManager.blockingGetAuthToken卡住了
AccountManager.blockingGetAuthToken stucks
我创建了一个AbstractThreadedSyncAdapter
通过Retrofit2与服务器同步数据。为了处理身份验证,我创建了一个拦截器来添加令牌。
当拦截器需要通过调用 accountManager.blockingGetAuthToken(...)
来刷新令牌时,它会卡住,直到抛出 android.accounts.OperationCanceledException
。当我在 AbstractAccountAuthenticator
-> getAuthToken
中放置断点时,在拦截器完成之前它不会被击中。
public class TokenInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
String oldToken = accountManager.peekAuthToken(accounts[0], LoginActivity.AUTH_TOKEN_TYPE);
if(newTokenRequired){
accountManager.invalidateAuthToken(LoginActivity.ACCOUNT_TYPE, oldToken);
}
String token = accountManager.blockingGetAuthToken(accounts[0], LoginActivity.AUTH_TOKEN_TYPE, true); // <-- stucks here
// add token to request and return response
}
}
这是AccountAuthenticator
中的getAuthToken
方法:
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) {
final AccountManager accountManager = AccountManager.get(context);
final String refreshToken = accountManager.getPassword(account);
final Bundle bundle = new Bundle();
if (refreshToken != null) {
try {
Response<RefreshTokenResponse> refreshTokenResponse = apiService.refreshToken(refreshToken).execute();
if (refreshTokenResponse.isSuccessful()) {
bundle.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
bundle.putString(AccountManager.KEY_AUTHTOKEN, refreshTokenResponse.body().getAccess_Token());
accountManager.setPassword(account, refreshTokenResponse.body().getRefresh_token());
return bundle;
}
} catch (Exception e) {
Log.e(AccountAuthenticator.class.getName(), "getAuthToken: failed to get access token", e);
}
}
// start login intent
final Intent intent = new Intent(context, LoginActivity.class);
intent.putExtra(LoginActivity.PARAM_USER_PASS, account.name);
intent.putExtra(LoginActivity.AUTH_TOKEN_TYPE, authTokenType);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}
我认为如何调用 blockingGetAuthToken
不是问题,因为万一我在拦截器中注释掉 invalidateAuthToken(...)
它 returns 一个缓存的令牌。
我在拦截器中发出另一个 HTTP 请求时会不会有问题? (apiService.getBlaBlaBla() -> tokenInterceptor -> getAuthToken -> apiService.refreshToken() )
问题是断点实际上被击中了,但是在不同的帧中。这样断点就把authenticator挡住了,拦截器超时了。
要在帧之间切换,请转到调试 -> 调试器 -> 帧,然后select 从下拉列表中选择合适的帧。
这里有更多信息:https://developer.android.com/studio/debug/index.html#stackFrames
我创建了一个AbstractThreadedSyncAdapter
通过Retrofit2与服务器同步数据。为了处理身份验证,我创建了一个拦截器来添加令牌。
当拦截器需要通过调用 accountManager.blockingGetAuthToken(...)
来刷新令牌时,它会卡住,直到抛出 android.accounts.OperationCanceledException
。当我在 AbstractAccountAuthenticator
-> getAuthToken
中放置断点时,在拦截器完成之前它不会被击中。
public class TokenInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
String oldToken = accountManager.peekAuthToken(accounts[0], LoginActivity.AUTH_TOKEN_TYPE);
if(newTokenRequired){
accountManager.invalidateAuthToken(LoginActivity.ACCOUNT_TYPE, oldToken);
}
String token = accountManager.blockingGetAuthToken(accounts[0], LoginActivity.AUTH_TOKEN_TYPE, true); // <-- stucks here
// add token to request and return response
}
}
这是AccountAuthenticator
中的getAuthToken
方法:
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) {
final AccountManager accountManager = AccountManager.get(context);
final String refreshToken = accountManager.getPassword(account);
final Bundle bundle = new Bundle();
if (refreshToken != null) {
try {
Response<RefreshTokenResponse> refreshTokenResponse = apiService.refreshToken(refreshToken).execute();
if (refreshTokenResponse.isSuccessful()) {
bundle.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
bundle.putString(AccountManager.KEY_AUTHTOKEN, refreshTokenResponse.body().getAccess_Token());
accountManager.setPassword(account, refreshTokenResponse.body().getRefresh_token());
return bundle;
}
} catch (Exception e) {
Log.e(AccountAuthenticator.class.getName(), "getAuthToken: failed to get access token", e);
}
}
// start login intent
final Intent intent = new Intent(context, LoginActivity.class);
intent.putExtra(LoginActivity.PARAM_USER_PASS, account.name);
intent.putExtra(LoginActivity.AUTH_TOKEN_TYPE, authTokenType);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}
我认为如何调用 blockingGetAuthToken
不是问题,因为万一我在拦截器中注释掉 invalidateAuthToken(...)
它 returns 一个缓存的令牌。
我在拦截器中发出另一个 HTTP 请求时会不会有问题? (apiService.getBlaBlaBla() -> tokenInterceptor -> getAuthToken -> apiService.refreshToken() )
问题是断点实际上被击中了,但是在不同的帧中。这样断点就把authenticator挡住了,拦截器超时了。
要在帧之间切换,请转到调试 -> 调试器 -> 帧,然后select 从下拉列表中选择合适的帧。
这里有更多信息:https://developer.android.com/studio/debug/index.html#stackFrames