2012-07-11 10 views
16

Sto creando una nuova app per Android utilizzando SyncAdapter per gestire la sincronizzazione db. Ho tutto a posto e l'app sta funzionando bene ma ho notato che sono connesso due volte.Accedi due volte quando usi SyncAdapters

Il primo accesso avviene quando la classe AuthenticatorActivity (che estende AccountAuthenticatorActivity) convalida l'utente e la password.

Se l'utente e la password sono corretti il ​​AuthenticatorActivity fa poi:

  • Se il account non esisteva lo crea utilizzando mAccountManager.addAccountExplicitly()
  • Il authToken viene salvato utilizzando intent.putExtra(AccountManager.KEY_AUTHTOKEN, authToken);

Questo è stato praticamente copiato/incollato dagli esempi Android, quindi immagino sia ok.

Il problema è che quando i SyncAdapter lanci e utilizza

authtoken = mAccountManager.blockingGetAuthToken(account, 
      AuthenticatorActivity.PARAM_AUTHTOKEN_TYPE, true); 

Il metodo getAuthToken() all'interno della classe Authenticator che si estende AbstractAccountAuthenticator chiamata. E all'interno di questo metodo sto colpendo ancora una volta l'endpoint di accesso.

Da questo momento in poi l'endpoint di accesso non viene colpito nuovamente fino allo scadere del authToken.

Questo non è qualcosa che mi infastidisce molto ma vorrei sapere se c'è un modo per evitare di effettuare il login due volte.

+0

Che dire dell'utilizzo di 'AccountManager.setAuthToken()' invece di rimandare il token nel pacchetto? – alexanderblom

+0

@alexanderblom: Ho provato anche quello. Nessuna differenza. – Macarse

+1

Penso che il motivo alla base di due accessi sia stato quello di garantire che i token di autenticazione siano aggiornati, ma non sono riuscito a trovare la fonte che mi supportasse. Ricordo di averlo letto da qualche parte quando ho cercato di seguire l'esempio C2DM quando è uscito l'anno scorso in Google I/O ... – Yenchi

risposta

14

Come si è visto, anche se Authenticator.java nel SampleSyncAdapter dice

La cosa interessante che questa classe dimostra è l'uso di authTokens come parte del processo di autenticazione. ... Se nell'account è già presente un authToken, viene restituito authToken. Se non lo facciamo, ma abbiamo un nome utente e una password, allora proveremo a parlare con il servizio di esempio per recuperare un authToken.

questa è una bugia. Authenticator.getAuthToken non esegue alcun controllo della cache, ma semplicemente fa clic sulla rete per ottenere un token.

La soluzione è quella di aggiungere nel controllo mancante:

Authenticator.java: 
@Override 
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, 
     String authTokenType, Bundle loginOptions) throws NetworkErrorException { 

    // check that authToken type supported 
    ... 

    // Check if we already have a cached token to return 
    final AccountManager am = AccountManager.get(mContext); 
    String cachedAuthToken = am.peekAuthToken(account, authTokenType); 
    if (cachedAuthToken != null) { 
     final Bundle result = new Bundle(); 
     result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); 
     result.putString(AccountManager.KEY_ACCOUNT_TYPE, Constants.ACCOUNT_TYPE); 
     result.putString(AccountManager.KEY_AUTHTOKEN, cachedAuthToken); 
     return result; 
    } 

    // Get new authToken from server 
    ... 

    // If all else fails, prompt the user for credentials 
    ... 
} 

Si noti che il resto del tuo progetto ha bisogno di utilizzare religiosamente AccountManager.invalidateAuthToken quando le chiamate non riescono altrimenti si finirà con un ciclo infinito di chiamate in mancanza , tentando di ottenere un nuovo token di autenticazione e quindi di non riuscire nuovamente quando viene restituito lo stesso token di autenticazione memorizzato nella cache.

+2

Se hai bisogno di un esempio operativo completo, ho usato la stessa tecnica sul mio adattatore di esempio Sync per il mio post sul blog su questo argomento. Potete vedere il codice di esempio applicazione qui: https://github.com/Udinic/SyncAdapter Il post blog su schede di sincronizzazione qui: http://udinic.wordpress.com/2013/07/24/ write-your-own-android-sync-adapter/ Specificamente su Authenticators, spiegando questo problema, è possibile leggere qui: http://udinic.wordpress.com/2013/04/24/write-your-own- android-authenticator/ – Udinic

+0

nella classe che estende AccountAuthenticatorActivity, chiamo am.setAuthToken (String) e am.setPassword (String). Nella classe che estende AbstractAccountAuthenticator, chiamo am.peekAuthToken (account, authTokenType); Questo restituisce null la prima volta. Quindi effettua l'accesso utilizzando il nome utente dall'account e la password salvata nel gestore account (am.getPassword (account)); E quindi authtoken viene salvato nel gestore account tramite il pacchetto restituito da getAuthToken. Qualcuno sa perché am.setPassword() imposta correttamente la password ma am.setAuthToken (String) no? – Ethan