![](/img/trans.png)
[英]How to request an auth token for multiple services using AccountManager?
[英]Make Volley request with AccountManager Token
我試圖讓Volley網絡庫與Android的AccountManager一起使用,我正在使用該庫來獲取REST API的身份驗證令牌。 我的基本想法是延遲實際的Volley請求(實際上是擴展默認請求的GSONRequest),直到從AccountManager中檢索到令牌為止(請參見下面的TokinzedGsonRequest)。 但是,這似乎不起作用-GC的運行方式非常瘋狂,並且該應用最終由於Stackoverflow錯誤而崩潰。 有任何想法嗎?
APIClient.java
public static void makeGsonRequest(Activity context, GsonRequest request, RequestQueue requestQueue) {
AccountManager accountManager = AccountManager.get(context);
Account account = getAccount(context, accountManager);
// Delay the request until a token is available
TokenizedGsonRequest futureRequest = new TokenizedGsonRequest(request, requestQueue);
Bundle options = new Bundle();
accountManager.getAuthToken(
account,
context.getResources().getString(R.string.authenticator_auth_type),
options,
context,
futureRequest,
null
);
}
TokenizedGsonRequest.java(實現AccountManagerCallback)
/**
* Wrapper around {@link .helpers.GsonRequest} for use with
* an {@link android.accounts.AccountManager}. The actual {@link com.android.volley.Request}
* is delayed until a token has been obtained.
*/
private static class TokenizedGsonRequest implements AccountManagerCallback<Bundle> {
public static final String TAG = TokenizedGsonRequest.class.getSimpleName();
private GsonRequest mRequest;
private RequestQueue mRequestQueue;
private TokenizedGsonRequest(GsonRequest request, RequestQueue requestQueue) {
this.mRequest = request;
this.mRequestQueue = requestQueue;
}
@Override
public void run(AccountManagerFuture<Bundle> result) {
Bundle bundle;
// todo authentication error
try {
bundle = result.getResult();
} catch (OperationCanceledException e) {
e.printStackTrace();
return;
} catch (AuthenticatorException e) {
e.printStackTrace();
return;
} catch (IOException e) {
e.printStackTrace();
return;
}
String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
if (!TextUtils.isEmpty(authToken)) {
Log.d(TAG, "Received authentication token " + authToken);
try {
// Since Volley request urls are final, we have to copy the existing one
String tokenUrl = API.appendQueryParameter(mRequest.getUrl(), API.TOKEN, authToken);
GsonRequest requestCopy = new GsonRequest<>(
mRequest.getMethod(),
tokenUrl,
mRequest.getClass(),
mRequest.getHeaders(),
mRequest.getRequestObject(),
mRequest.getListener(),
mRequest.getErrorListener() // todo wrap error listener for retry on 400
);
// Retain the original request tag for cancellation
requestCopy.setTag(TAG);
mRequestQueue.add(requestCopy);
} catch (AuthFailureError e) {
Log.d(TAG, e.getMessage());
// todo bubble up
}
} else {
// todo authentication error
}
}
}
我自己解決了問題,並通過使用基於標頭的身份驗證將身份驗證的Volley請求與AccountManager集成在一起。 事實證明,使用GSON時克隆請求的效果不佳,因為參數化信息會丟失。
APIClient.java
/**
* Ensures to authenticate a given {@link com.android.volley.Request} through the
* {@link android.accounts.AccountManager}.
* @param request
* @param listener
*/
public void makeRequest(Request request, AuthenticatedRequestCallback.AuthenticationErrorListener listener) {
AccountManager accountManager = AccountManager.get(mContext);
Account account = getAccount(accountManager);
// Delay the request until a token from the account manager is available
request.setRetryPolicy(new TokenRetryPolicy());
AuthenticatedRequestCallback requestCallback = new AuthenticatedRequestCallback(
request, mRequestQueue, listener);
// Retrieve token and execute initial request
Bundle options = new Bundle();
accountManager.getAuthToken(
account,
mContext.getResources().getString(R.string.authenticator_auth_type),
options,
mContext,
requestCallback,
null
);
}
AuthenticatedRequestCallback.java
/**
* Wrapper around {@link com.votilab.votiapp.helpers.GsonRequest} for use with
* an {@link android.accounts.AccountManager}. The actual {@link com.android.volley.Request}
* is executed when an authentication token has been obtained.
*/
public class AuthenticatedRequestCallback implements AccountManagerCallback<Bundle> {
public static final String TAG = AuthenticatedRequestCallback.class.getSimpleName();
public static final String AUTH_TOKEN_PARAM = "token";
public static final String AUTH_TOKEN_HEADER = "X-Auth-Token";
private Request mRequest;
private RequestQueue mRequestQueue;
private final AuthenticationErrorListener mErrorListener;
/**
* Callback interface to listen for errors thrown by the
* {@link android.accounts.AccountManager}.
*/
public interface AuthenticationErrorListener {
public void onAuthenticationError(AuthenticatorException e);
}
public AuthenticatedRequestCallback(Request request,RequestQueue requestQueue,
AuthenticationErrorListener listener) {
this.mRequest = request;
this.mRequestQueue = requestQueue;
this.mErrorListener = listener;
}
@Override
public void run(AccountManagerFuture<Bundle> result) {
Bundle bundle;
try {
bundle = result.getResult();
} catch (OperationCanceledException | IOException e) {
if (mErrorListener != null) {
mErrorListener.onAuthenticationError(new AuthenticatorException(e.getMessage()));
}
return;
} catch (AuthenticatorException e) {
if (mErrorListener != null) {
mErrorListener.onAuthenticationError(e);
}
return;
}
String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
if (!TextUtils.isEmpty(authToken)) {
Log.d(TAG, "Received authentication token " + authToken); // todo remove log message
try {
((AuthorizableRequest) mRequest)
.addHeader(AUTH_TOKEN_HEADER, authToken);
} catch (ClassCastException e) {
throw new ClassCastException(mRequest.toString()
+ " must implement " + AuthorizableRequest.class.getSimpleName());
}
// Queue the request for execution
mRequestQueue.add(mRequest);
} else {
if (mErrorListener != null) {
mErrorListener.onAuthenticationError(
new AuthenticatorException("Authentication token is empty."));
}
}
}
}
AuthorizableRequest.java
/**
* An interface for implementation in a {@link com.android.volley.Request} to
* support custom authentication headers.
*/
public interface AuthorizableRequest {
public void addHeader(String header, String value);
}
假設擴展了AbstractAccountAuthenticator的自定義Authenticator的正確實現,您應該能夠在進行身份驗證的同時從這樣的活動發出請求:
mApiClient.makeRequest(someVolleyRequest, new AuthenticatedRequestCallback.AuthenticationErrorListener() {
@Override
public void onAuthenticationError(AuthenticatorException e) {
// something went wrong in the account manager
}
});
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.