簡體   English   中英

使用AccountManager令牌發出Volley請求

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM