Asynchronous account authentication with Volley

2019-02-17 19:22发布

everybody.

I'm implementing an account authenticator using AbstractAccountAuthenticator and I need call an asynchronous method in the function getAuthToken, to authenticate a user. My code is like this:

public class AccountAuthenticator extends AbstractAccountAuthenticator {
    ...
    @Override
    public Bundle getAuthToken( final AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options )
        throws NetworkErrorException 
    {
        final AccountManager accountManager = AccountManager.get(context);   
        String authToken = accountManager.peekAuthToken( account, authTokenType );   
        // !!!!
        if( TextUtils.isEmpty(authToken) ) {
            <<call asynchronous method to acquire token>>
            return null;  
        }
        // !!!!
        final Bundle result = new Bundle();
        result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
        result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
        result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
        return result;
    }
    ...
}

According to Google's documentation for the 'getAuthToken' method: it returns a Bundle result or null if the result is to be returned via the response. The result will contain either:
AccountManager.KEY_INTENT, or
AccountManager.KEY_ACCOUNT_NAME, AccountManager.KEY_ACCOUNT_TYPE, and AccountManager.KEY_AUTHTOKEN, or
AccountManager.KEY_ERROR_CODE and AccountManager.KEY_ERROR_MESSAGE to indicate an error

And I need to return null because the authenticator method is asynchronous, but how I return the Bundle via the 'response' parameter, according to the documentation?
Thanks for all, and sorry my english.

1条回答
够拽才男人
2楼-- · 2019-02-17 19:35

Yes, I found the solution. You must use the 'response' parameter to return the results. Below is the source that I use in my application. I hope this can help.



    public Bundle getAuthToken( final AccountAuthenticatorResponse response, final Account account, String authTokenType, Bundle options )
    throws NetworkErrorException 
    {
        final Bundle result = new Bundle();
        // We will not allow authentication for a type of account not used by the service.
        if( false == authTokenType.equals(Accounts.TokenTypes.User) ) {
            result.putString( AccountManager.KEY_ERROR_MESSAGE, context.getString(R.string.error_invalid_auth_token_type) );
            return result;
        }

        final AccountManager accountManager = AccountManager.get(context);
        String authToken = accountManager.peekAuthToken( account, authTokenType );
        Token token = null;
        // If the account already has an authorization key ...
        if( ! TextUtils.isEmpty(authToken) ) 
        {
            // ...load its details from the userdata's account.
            String tokenStr = accountManager.getUserData( account, Token.class.getName() );
            JSONObject tokenJson = null;
            try {
                tokenJson = new JSONObject( tokenStr );
                token = new Token( tokenJson );
            }
            catch( JSONException e ) {
                token = new Token();
            }
        }
        // But if the key is invalid or expired ...
        if( token == null || token.isExpired() ) 
        {
            // ...loads the account user credentials to try to authenticate it.
            new SignInRequest( new Client(), account.name, accountManager.getPassword(account),
                new Response.Listener() {
                    @Override
                    public void onResponse( Token token ) {
                        /*
                        Response: a Bundle result or null if the result is to be returned via the response. 
                        The result will contain either: 
                        • AccountManager.KEY_INTENT (!!qdo envia o bundle para uma atividade!!), or 
                        • AccountManager.KEY_ACCOUNT_NAME, AccountManager.KEY_ACCOUNT_TYPE, and AccountManager.KEY_AUTHTOKEN, or 
                        • AccountManager.KEY_ERROR_CODE and AccountManager.KEY_ERROR_MESSAGE to indicate an error
                        */
                        result.putString( AccountManager.KEY_ACCOUNT_NAME, account.name );
                        result.putString( AccountManager.KEY_ACCOUNT_TYPE, account.type );
                        result.putString( AccountManager.KEY_AUTHTOKEN, token.getAccessToken() );
                        response.onResult( result );
                    }
                }
                , 
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError volleyError) {
                        int errorCode = (volleyError.networkResponse == null ? -1 : volleyError.networkResponse.statusCode);
                        String errorMessage = null;
                        if( volleyError.getLocalizedMessage() != null )
                            errorMessage = volleyError.getLocalizedMessage();
                        else if( volleyError.getMessage() != null )
                            errorMessage = volleyError.getMessage();
                        else
                            errorMessage = volleyError.toString();
                        result.putInt( AccountManager.KEY_ERROR_CODE, errorCode );
                        result.putString( AccountManager.KEY_ERROR_MESSAGE, errorMessage );
                        response.onError( errorCode, errorMessage );
                    }
                }
            ).execute( this.context );
            // Returns null because we use the response parameter. See callbacks above.
            return null;
        }
        // Otherwise, the key is valid, it returns.
        result.putString( AccountManager.KEY_ACCOUNT_NAME, account.name );
        result.putString( AccountManager.KEY_ACCOUNT_TYPE, account.type );
        result.putString( AccountManager.KEY_AUTHTOKEN, authToken );
        return result;
    }


查看更多
登录 后发表回答