2015-09-16 37 views
0

我正在嘗試使用Android的AccountManager來處理排球網絡庫,該API用於爲我的REST API獲取身份驗證令牌。我的基本想法是延遲實際的Volley請求(實際上GSSONRequest擴展默認的請求),直到從AccountManager中檢索到令牌(請參閱下面的TokinzedGsonRequest)。然而,這似乎並沒有工作 - GC工作得像瘋了一樣,應用程序最終因Stackoverflow錯誤而崩潰。有任何想法嗎?使用AccountManager令牌對排球請求進行排序

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 
     } 
    } 
} 

回答

0

我解決了它自己和管理使用報頭 - 與驗證的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自定義驗證器的正確實施,你應該能夠從你的活動這樣的認證,同時提出請求是照顧幕後:

mApiClient.makeRequest(someVolleyRequest, new AuthenticatedRequestCallback.AuthenticationErrorListener() { 
       @Override 
       public void onAuthenticationError(AuthenticatorException e) { 
        // something went wrong in the account manager 
       } 
      });