第一次在 AccountManager 中添加帐户时未触发 AccountAuthenticatorActivity

Posted

技术标签:

【中文标题】第一次在 AccountManager 中添加帐户时未触发 AccountAuthenticatorActivity【英文标题】:AccountAuthenticatorActivity not getting triggered for adding account for first time in AccountManager 【发布时间】:2015-12-27 09:08:29 【问题描述】:

我正在开发基于 XMPP 的 android 聊天客户端。它适用于 Socket 连接,并且需要定期 ping 到服务器以保持连接活动。为此,我正在使用后台运行的 android 服务。

在此服务中创建一个连接实例。该服务永远存在于后台并执行 ping 和接收消息的工作。

一个问题是当聊天客户端从任务管理器中被杀死时,后台服务重新启动(它仍然继续运行,因为我已将其声明为 START-STICKY)。

一旦服务重新启动,它就会清除连接实例。这对我来说是个问题,因为连接实例被清除相当于连接丢失。

所以我正在重新创建连接,如果它在服务的 onStartCommand 上为空。

但是要建立连接,我需要用户名和密码。我想将这些凭据保存在 Android 的 AccountManager 中。 (我觉得在 AccountManager 中存储密码比使用 SharedPreferrence 或 SQLite 或 FileSystem 更安全)

1.由于我没有存储任何身份验证令牌,因此在我的情况下使用 AccountManager 是正确的选择吗?真的比其他存储更安全吗?

2。有什么方法可以像在帐户管理器中存储身份验证令牌一样存储连接实例本身?

根据我对 Account Manager 的阅读,我已经实现了 AuthenticationService、AccountAuthenticator 和 AccountAuthenticator Activity,如下所示-

身份验证服务:

public class AuthenticatationService extends Service 

@Override
public IBinder onBind(Intent intent) 
    // TODO Auto-generated method stub
    return new AccountAuthenticator(this).getIBinder();


账户验证器:

public class AccountAuthenticator extends AbstractAccountAuthenticator 
Context mContext;
public AccountAuthenticator(Context context) 
    super(context);
    mContext=context;


@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) 
    return null;


@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException 

    Log.d("Mymsg", "AccountAuthenticator > addAccount() called with " + "response = [" + response + "], accountType = [" + accountType + "], authTokenType = [" + authTokenType + "], requiredFeatures = [" + requiredFeatures + "], options = [" + options + "]" + "---->START");

    final Intent intent = new Intent(mContext, LoginActivity.class);

    intent.putExtra(mContext.getString(R.string.intentdatakey_accounttype), accountType);
    intent.putExtra(mContext.getString(R.string.intentdatakey_authtype), authTokenType);
    intent.putExtra(mContext.getString(R.string.intentdatakey_isaddingnewaccount), true);

    intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
    final Bundle bundle = new Bundle();
    bundle.putParcelable(AccountManager.KEY_INTENT, intent);

    Log.d("Mymsg", "AccountAuthenticator > addAccount() returned [bundle: " + bundle + "----> STOP");
    return bundle;


@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException 
    return null;


@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException 

    Log.d("Mymsg", "AccountAuthenticator > getAuthToken() called with " + "response = [" + response + "], account = [" + account + "], authTokenType = [" + authTokenType + "], options = [" + options + "]" + "---->START");
    // Extract the username and password from the Account Manager, and ask
    // the server for an appropriate AuthToken.
    final AccountManager am = AccountManager.get(mContext);

    String authToken = am.peekAuthToken(account, authTokenType);

    // Lets give another try to authenticate the user
    if (TextUtils.isEmpty(authToken)) 
        final String password = am.getPassword(account);
        if (password != null) 
            authToken = password;
        
    

    // If we get an authToken - we return it
    if (!TextUtils.isEmpty(authToken)) 
        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;
    

    // If we get here, then we couldn't access the user's password - so we
    // need to re-prompt them for their credentials. We do that by creating
    // an intent to display our AuthenticatorActivity.
    final Intent intent = new Intent(mContext, LoginActivity.class);
    intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
    intent.putExtra(mContext.getString(R.string.intentdatakey_accounttype), account.type);
    intent.putExtra(mContext.getString(R.string.intentdatakey_authtype), authTokenType);
    final Bundle bundle = new Bundle();
    bundle.putParcelable(AccountManager.KEY_INTENT, intent);

    Log.d("Mymsg", "AccountAuthenticator > getAuthToken() returned [bundle: " + bundle + "----> STOP");
    return bundle;


@Override
public String getAuthTokenLabel(String authTokenType) 
    return null;


@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException 
    return null;


@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException 
    return null;


登录活动:

public class LoginActivity extends AccountAuthenticatorActivity implements View.OnClickListener 

private EditText txtUsername;
private Button btnLogin;
private String username;

private ProgressDialog progressDialog;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_login);

    txtUsername = (EditText) findViewById(R.id.txtUsername);

    btnLogin = (Button) findViewById(R.id.btnLogin);
    btnLogin.setOnClickListener(this);

    progressDialog = new ProgressDialog(this);

    // Connection and Login Response events
    LocalBroadcastManager.getInstance(this).registerReceiver(connectionSuccessfulReceiver,
            new IntentFilter(getString(R.string.broadcastmessage_connectionsuccessful)));
    LocalBroadcastManager.getInstance(this).registerReceiver(connectionFailureReceiver,
            new IntentFilter(getString(R.string.broadcastmessage_connectionfailure)));


@Override
public boolean onCreateOptionsMenu(Menu menu) 
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;


@Override
public boolean onOptionsItemSelected(MenuItem item) 
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) 
        return true;
    

    return super.onOptionsItemSelected(item);


@Override
public void onClick(View v) 
    switch (v.getId()) 
        case R.id.btnLogin:

            Log.d("Mymsg", "LoginActivity > onClick() called with " + "v = [" + v + "]" + "---->START");

            username = txtUsername.getText().toString();

            if (username.isEmpty()) 
                Toast.makeText(this, "Please enter username", Toast.LENGTH_LONG);
                return;
            

            progressDialog.setMessage("Logging In...");
            progressDialog.setCancelable(false);
            progressDialog.show();

            final Intent res = new Intent();
            res.putExtra(AccountManager.KEY_ACCOUNT_NAME, username);
            res.putExtra(AccountManager.KEY_ACCOUNT_TYPE, getString(R.string.accounttype));
            res.putExtra(AccountManager.KEY_AUTHTOKEN, username);
            res.putExtra(AccountManager.KEY_PASSWORD, username);

            final Account account = new Account(username, getString(R.string.accounttype));
            if (getIntent().getBooleanExtra(getString(R.string.intentdatakey_isaddingnewaccount), false)) 
                String authtoken = username;
                String authtokenType = getString(R.string.authtype);
                // Creating the account on the device and setting the auth token we got
                // (Not setting the auth token will cause another call to the server to authenticate the user)
                AccountManager.get(this).addAccountExplicitly(account, username, null);
                AccountManager.get(this).setAuthToken(account, authtokenType, authtoken);
             else 
                AccountManager.get(this).setPassword(account, username);
            
            setAccountAuthenticatorResult(res.getExtras());
            setResult(RESULT_OK, res);

            Log.d("Mymsg", "LoginActivity > onClick() ----> STOP");

            break;
        default:
            break;
    


/**
 * On Connection Successful
 */
public void onConnectionSuccessful() 
    Log.d("Mymsg", "LoginActivity > onConnectionSuccessful() called with " + "" + "---->START");

    runOnUiThread(new Runnable() 
        @Override
        public void run() 
            if (progressDialog.isShowing()) 
                progressDialog.dismiss();
            
        
    );

    finish();
    Log.d("Mymsg", "LoginActivity > onConnectionSuccessful() ----> STOP");


/**
 * On Connection Failure
 *
 * @param message- error message
 */
public void onConnectionFailure(final String message) 
    Log.d("Mymsg", "LoginActivity > onConnectionFailure() called with " + "message = [" + message + "]" + "---->START");
    runOnUiThread(new Runnable() 
        @Override
        public void run() 

            if (progressDialog.isShowing()) 
                progressDialog.dismiss();
            

            Toast.makeText(LoginActivity.this, "Failed connecting: " + message, Toast.LENGTH_LONG).show();
        
    );

    Log.d("Mymsg", "LoginActivity > onConnectionFailure() ----> STOP");



/*
* Recievers for Connection Events
 */
private BroadcastReceiver connectionSuccessfulReceiver = new BroadcastReceiver() 
    @Override
    public void onReceive(Context context, Intent intent) 
        Log.d("Mymsg", "LoginActivity > connectionSuccessfulReceiver > onReceive() called with " + "context = [" + context + "], intent = [" + intent + "]" + "---->START");
        onConnectionSuccessful();
        Log.d("Mymsg", "LoginActivity > connectionSuccessfulReceiver > onReceive() returned void ----> STOP");
    
;

private BroadcastReceiver connectionFailureReceiver = new BroadcastReceiver() 
    @Override
    public void onReceive(Context context, Intent intent) 
        Log.d("Mymsg", "LoginActivity > connectionFailureReceiver > onReceive() called with " + "context = [" + context + "], intent = [" + intent + "]" + "---->START");
        onConnectionFailure(intent.getStringExtra(getString(R.string.intentdatakey_failuremessage)));
        Log.d("Mymsg", "LoginActivity > connectionFailureReceiver > onReceive() ----> STOP");
    
;

在恢复每个活动时,我检查了连接是否为空,如果是,我已经启动了该后台服务(我之前提到过保持连接并定期 ping)。如果 AccountManager 中存在 Account,此服务将建立连接并回调服务。

3.我面临的问题是 AccountManager 中尚不存在用户名和密码。根据我的理解,getAuthToken 是 Authenticator 中的入口点,如果不存在身份验证令牌,则调用登录活动。但是,如果用户名不存在如何调用 getAuthToken。需要建议。

以下是我的后台服务-

 public class XMPPListener extends Service implements ChatMessageListener, ChatManagerListener, ConnectionListener 

private static XMPPListener instance;
private NotificationCompat.Builder mBuilder;
private NotificationManager mNotificationManager;
private Context mContext;
private AbstractXMPPConnection connection;
private static boolean isConnected = false;
private static boolean isLoggedIn = false;
private User user;

public XMPPListener() 
    Log.d("Mymsg", "XMPPListener > XMPPListener() called with " + "" + "---->START");
    mContext = this;
    instance = this;
    Log.d("Mymsg", "XMPPListener > XMPPListener() > this: " + this);

    //To show notification when app is not foreground
    mBuilder = new NotificationCompat.Builder(mContext);

    Log.d("Mymsg", "XMPPListener > XMPPListener() ----> STOP");


/**
 * Get Singleton instance
 *
 * @return instance
 */
public static XMPPListener getInstance(Context context) 

    return instance;


@Override
public void processMessage(Chat chat, Message message) 
    Log.d("Mymsg", "XMPPListener > processMessage() called with " + "message = [" + message + "]" + "---->START");

    try 
        // Make entry of message in local database
        makeEntryOfMessageInLocalDatabase(chat, message);

        // Show Notification
        showNotification(chat, message);

        // Broadcast message that new message received
        LocalBroadcastManager.getInstance(mContext).sendBroadcast(new Intent(getString(R.string.localbroadcastaction_messagereceived)));
     catch (SQLException e) 
        e.printStackTrace();
    

    Log.d("Mymsg", "XMPPListener > processMessage() returned void ----> STOP");


/**
 * Show Notification
 *
 * @param chat
 * @param message
 */
private void showNotification(Chat chat, Message message) 
    mBuilder.setSmallIcon(R.drawable.notification_icon);
    mBuilder.setContentTitle("@" + chat.getParticipant() + " messaged");
    mBuilder.setContentText(message.getBody());

    Intent resultIntent = new Intent(mContext, ChatWindowActivity.class);
    TaskStackBuilder stackBuilder = TaskStackBuilder.create(mContext);
    stackBuilder.addParentStack(ChatWindowActivity.class);

    // Adds the Intent that starts the Activity to the top of the stack
    stackBuilder.addNextIntent(resultIntent);
    PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
    mBuilder.setContentIntent(resultPendingIntent);

    // notificationID allows you to update the notification later on.
    mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    mNotificationManager.notify(1, mBuilder.build());


/**
 * Make entry of message in Local Database
 *
 * @param chat
 * @param message
 * @throws SQLException
 */
private void makeEntryOfMessageInLocalDatabase(Chat chat, Message message) throws SQLException 

    Log.d("Mymsg", "XMPPListener > makeEntryOfMessageInLocalDatabase() called with " + "chat = [" + chat + "], message = [" + message + "]" + "---->START");

    ChatMessage chatMessage = new ChatMessage();

    // This is received message
    chatMessage.setSent(false);

    chatMessage.setThreadId(chat.getThreadID());
    chatMessage.setChatmessage(message.getBody());
    chatMessage.setTimeStamp(new Date());
    chatMessage.setIsRead(false);

    ChatThread chatThread = new ChatThread();
    chatThread.setThreadId(chat.getThreadID());
    chatThread.setUsername(chat.getParticipant());
    chatThread.setLastChatmessage(message.getBody());
    chatThread.setLastMessageTimeStamp(new Date());
    chatThread.setLastMessageSent(false);
    chatThread.setUnreadMessageCount(SampleChatClientAppDataOperations.getInstance(mContext).getUnReadMessageCountInThread(chat.getThreadID()));

    // Make database entry
    SampleChatClientAppData sampleChatClientAppData = new SampleChatClientAppData(mContext);

    // Update messages table
    sampleChatClientAppData.getChatMessageDao().create(chatMessage);

    // Update Chat list table
    sampleChatClientAppData.getChatThreadDao().update(chatThread);

    Log.d("Mymsg", "XMPPListener > makeEntryOfMessageInLocalDatabase() returned void ----> STOP");


@Override
public IBinder onBind(Intent intent) 
    return null;


@Override
public void onCreate() 
    Log.d("Mymsg", "XMPPListener > onCreate() called with " + "" + "---->START");
    super.onCreate();

    Log.d("Mymsg", "XMPPListener > onCreate() ----> STOP");


@Override
public int onStartCommand(Intent intent, int flags, int startId) 
    Log.d("Mymsg", "XMPPListener > onStartCommand() called with " + "intent = [" + intent + "], flags = [" + flags + "], startId = [" + startId + "]" + "---->START");

    if (connection == null || !connection.isConnected() || !connection.isAuthenticated()) 
        new GetAuthToken().execute();
    

    int returnValue = super.onStartCommand(intent, flags, startId);
    Log.d("Mymsg", "XMPPListener > onStartCommand() returned [returnValue: " + returnValue + "----> STOP");
    return returnValue;


@Override
public void onTaskRemoved(Intent rootIntent) 
    Log.d("Mymsg", "XMPPListener > onTaskRemoved() called with " + "rootIntent = [" + rootIntent + "]" + "---->START");
    super.onTaskRemoved(rootIntent);
    Log.d("Mymsg", "XMPPListener > onTaskRemoved() ----> STOP");


@Override
public void onDestroy() 
    Log.d("Mymsg", "XMPPListener > onDestroy() called with " + "" + "---->START");
    super.onDestroy();
    Log.d("Mymsg", "XMPPListener > onDestroy() ----> STOP");


/**
 * Getter for connection
 *
 * @return instance of connection
 */
public AbstractXMPPConnection getConnection() 
    return connection;


@Override
public void chatCreated(final Chat chat, boolean createdLocally) 

    Log.d("Mymsg", "XMPPListener > chatCreated() called with " + "chat = [" + chat + "], createdLocally = [" + createdLocally + "]" + "---->START");

    if (!createdLocally) 

        // Register listener
        chat.addMessageListener(XMPPListener.getInstance(this));


        checkIfChatThreadExistForUser(chat.getParticipant(), new Handler() 
            @Override
            public void handleMessage(android.os.Message msg) 
                Log.d("Mymsg", "XMPPListener > checkIfChatThreadExistForUser > handleMessage() called with " + "msg = [" + msg + "]" + "---->START");
                super.handleMessage(msg);
                ArrayList<ChatThread> chatThread;
                try 
                    chatThread = (ArrayList<ChatThread>) msg.obj;

                    Log.d("Mymsg", "chatThread: " + chatThread);

                    if (chatThread.isEmpty()) 
                        // If first time chatting with this user create new thread;
                        createChatThreadInLocalDb(chat);
                    

                 catch (ClassCastException e) 
                    e.printStackTrace();
                

                Log.d("Mymsg", "XMPPListener > checkIfChatThreadExistForUser > handleMessage() returned null----> STOP");
            
        , new Handler() 
            @Override
            public void handleMessage(android.os.Message msg) 
                super.handleMessage(msg);
            
        );
    


    Log.d("Mymsg", "XMPPListener > chatCreated() returned void----> STOP");


/**
 * Create chat thread in local db
 *
 * @param chat
 */
public void createChatThreadInLocalDb(Chat chat) 

    Log.d("Mymsg", "XMPPListener > createChatThreadInLocalDb() called with " + "chat = [" + chat + "]" + "---->START");

    ChatThread newChatThread = new ChatThread();
    newChatThread.setUsername(chat.getParticipant());
    newChatThread.setThreadId(chat.getThreadID());

    Datasource.addChatThread(this, newChatThread, new Handler() 
        @Override
        public void handleMessage(android.os.Message msg) 

            Log.d("Mymsg", "XMPPListener > addChatThread > createChatThreadInLocalDb > handleMessage() called with " + "msg = [" + msg + "]" + "---->START");
            super.handleMessage(msg);

            Integer newChatThreadCreated = 0;

            try 
                newChatThreadCreated = (Integer) msg.obj;

                if (newChatThreadCreated > 0) 

                    // Broadcast that chat is created
                    LocalBroadcastManager.getInstance(XMPPListener.this).sendBroadcast(new Intent(getString(R.string.localbroadcastaction_chatcreated)));
                
             catch (ClassCastException e) 
                e.printStackTrace();
            

            Log.d("Mymsg", "XMPPListener > addChatThread > handleMessage() ----> STOP");
        
    , new Handler() 
        @Override
        public void handleMessage(android.os.Message msg) 
            super.handleMessage(msg);
        
    );

    Log.d("Mymsg", "XMPPListener > createChatThreadInLocalDb() returned null----> STOP");


/**
 * Checks if thread exist for the username else
 * create new one
 *
 * @param fullUsername
 */
public void checkIfChatThreadExistForUser(final String fullUsername, final Handler successHandler, final Handler failureHandler) 
    Datasource.getChatThreadOfUser(this, fullUsername, successHandler, failureHandler);


// Internal sync task for connecting to XMPP server
class ConnectXMPP extends AsyncTask<User, Void, Exception> 

    @Override
    protected Exception doInBackground(User... params) 

        Log.d("Mymsg", "ConnectXMPP > doInBackground() called with " + "params = [" + params + "]" + "---->START");

        User user = params[0];

        Log.d("Mymsg", "user: " + user);

        XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder()
                .setUsernameAndPassword(user.getUsername(), user.getPassword())
                .setServiceName(mContext.getString(R.string.openfire_host))
                .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
                .setDebuggerEnabled(true)
                .build();

        SASLAuthentication.blacklistSASLMechanism("SCRAM-SHA-1");
        SASLAuthentication.blacklistSASLMechanism("DIGEST-MD5");
        SASLAuthentication.unBlacklistSASLMechanism("PLAIN");

        connection = new XMPPTCPConnection(config);

        try 
            connection.connect();
            isConnected = connection.isConnected();
            Log.d("Mymsg", "isConnected: " + isConnected);

            connection.login();
            isLoggedIn = connection.isAuthenticated();
            Log.d("Mymsg", "isLoggedIn: " + isLoggedIn);

            connection.addConnectionListener(XMPPListener.this);

            // Broadcast message that new message received
            LocalBroadcastManager.getInstance(mContext).sendBroadcast(new Intent(getString(R.string.broadcastmessage_connectionsuccessful)));

         catch (SmackException e) 
            e.printStackTrace();
            Intent broadcastIntent = new Intent(getString(R.string.broadcastmessage_connectionfailure));
            broadcastIntent.putExtra(getString(R.string.intentdatakey_failuremessage), e.getMessage());
            LocalBroadcastManager.getInstance(mContext).sendBroadcast(broadcastIntent);
         catch (IOException e) 
            e.printStackTrace();
            Intent broadcastIntent = new Intent(getString(R.string.broadcastmessage_connectionfailure));
            broadcastIntent.putExtra(getString(R.string.intentdatakey_failuremessage), e.getMessage());
            LocalBroadcastManager.getInstance(mContext).sendBroadcast(broadcastIntent);
         catch (XMPPException e) 
            e.printStackTrace();
            Intent broadcastIntent = new Intent(getString(R.string.broadcastmessage_connectionfailure));
            broadcastIntent.putExtra(getString(R.string.intentdatakey_failuremessage), e.getMessage());
            LocalBroadcastManager.getInstance(mContext).sendBroadcast(broadcastIntent);
        

        return null;
    

    @Override
    protected void onPostExecute(Exception e) 
        super.onPostExecute(e);
    


class GetAuthToken extends AsyncTask<Void, Void, Exception> 

    private User user;

    @Override
    protected Exception doInBackground(Void... params) 

        String username = "";
        String password = "";

        AccountManager accountManager = AccountManager.get(XMPPListener.this);
        final Account[] accounts = accountManager.getAccountsByType(getString(R.string.accounttype));

        Account account;
        if (accounts.length <= 0) 
            account = new Account(username, getString(R.string.accounttype));

         else 
            account = accounts[0];
        

        try 
            Bundle bundle = accountManager.getAuthToken(account, mContext.getString(R.string.authtype), true, null, null).getResult();
            password = (String) bundle.get(AccountManager.KEY_AUTHTOKEN);
            Log.d("Mymsg", "password: " + password);
            username = password;
         catch (OperationCanceledException e) 
            e.printStackTrace();
         catch (IOException e) 
            e.printStackTrace();
         catch (AuthenticatorException e) 
            e.printStackTrace();
        

        if (username.isEmpty() || password.isEmpty()) 
            Intent broadCastIntent = new Intent(getString(R.string.broadcastmessage_connectionfailure));
            broadCastIntent.putExtra(getString(R.string.intentdatakey_failuremessage), getString(R.string.errormessage_usercredentialmissing));
            LocalBroadcastManager.getInstance(mContext).sendBroadcast(broadCastIntent);
            return null;
        

        user = new User();
        user.setUsername(username);
        user.setPassword(password);

        return null;
    

    @Override
    protected void onPostExecute(Exception e) 
        super.onPostExecute(e);

        if (user != null) 
            // Connect XMPP
            new ConnectXMPP().execute(user);
        
    


【问题讨论】:

你可能应该把它分成多个问题。另外,“存储连接实例”到底是什么意思? 【参考方案1】:

1.由于我没有存储任何身份验证令牌,因此在我的情况下使用 AccountManager 是正确的选择吗?真的比其他存储更安全吗?

它本身并不安全。客户经理只是将用户名、密码和授权令牌以“明文”形式存储在 SQLite 数据库中。 因此,如果您有 root 访问权限,您通常可以轻松访问这些值。

如果您打算使用同步适配器机制,或者如果您打算与某些内容提供者(如联系人提供者或日历提供者)同步,则需要有一个身份验证器。

除此之外,它还为多个帐户提供了简单的支持,并提供了一个干净的界面来检索身份验证凭据。后者很好地将帐户管理/身份验证部分与应用程序的其余部分分离。

回答第一个问题:视情况而定。如果您不熟悉 Authenticator 并且您只是寻求更好的安全性,那么最好不要使用 Authenticator。

3.我面临的问题是当 AccountManager 中不存在用户名和密码时。根据我的理解,getAuthToken 是 Authenticator 中的入口点,如果不存在身份验证令牌,则调用登录活动。但是,如果用户名不存在如何调用 getAuthToken。需要建议。

这不是它的工作原理。您只能为现有帐户致电 getAuthTokengetAuthToken 仅在您的身份验证器返回 Intent 时调用活动。这样做的一个用例是使用 OAuth2 并且用户撤销了对您的应用程序的访问权限。在这种情况下,您需要提示用户再次授予访问权限。

请注意,其他应用也可能会请求身份验证令牌(假设它们具有所需的权限),因此请确保您没有将纯文本密码作为身份验证令牌返回或确保其他应用不会t 从您的身份验证器接收令牌。

因此,在调用 getAuthToken 时从身份验证器返回 Intent 的另一个用例是,如果您想提示用户获得许可。

那么它是如何完成的:

您的应用应首先检查是否存在现有帐户(使用getAccountsByType)。如果有一个你可以打电话给getAuthToken,如果有多个帐户你通常会问用户如果他之前没有选择任何一个,那么你为那个帐户打电话给getAuthToken,否则打电话给addAccount让用户先添加一个帐户。

如果您不想授予其他应用访问您的纯文本密码的权限,请考虑使用 setPassword 和 getPassword 而不是使用身份验证令牌。密码对您的应用程序是私有的,其他应用程序无法检索(嗯,至少这更困难)。或者考虑根本不使用 Authenticator。在您的情况下增加复杂性可能不值得。

【讨论】:

以上是关于第一次在 AccountManager 中添加帐户时未触发 AccountAuthenticatorActivity的主要内容,如果未能解决你的问题,请参考以下文章

在 Android 上重命名帐户 (AccountManager)

我可以使用 AccountManager 让用户使用他们的 Google 帐户登录吗?

AccountManager 的应用流程

Android setAuthToken 第一次无法正常工作

AccountManager:invalidateAuthToken 不会使令牌失效

使用 Account Manager 在 Android 中实现 JWT 身份验证