AccountManager:什么时候设置结果?

AccountManager: when to set result?

上下文

我的应用程序只存储 user/pass。没有使用令牌。

问题 1

关于结果的方法setAccountAuthenticatorResult(Bundle) and onResult(Bundle) are meant to notify the AbstractAccountAuthenticator,但我有一个项目没有它们,它们有什么用?

问题 2

onRequestContinued() 是什么意思?

问题 3

addAccount 完成并创建帐户时,是否应该在触发它的 Activity 上调用 onActivityResult

问题 4

如果使用键 AccountManager.KEY_INTENT in addAccount implementation, the AbstractAccountAuthenticator 返回 Intent 将启动 Intent。我注意到许多开发人员添加了额外功能。谁得到它们?

public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException
{
    Intent intent = new Intent(mContext, AuthenticatorActivity.class);
    intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);

    intent.putExtra(AuthenticatorActivity.ARG_ACCOUNT_TYPE, accountType);     // <-- this
    intent.putExtra(AuthenticatorActivity.ARG_AUTH_TYPE, authTokenType);      // <-- this
    intent.putExtra(AuthenticatorActivity.ARG_IS_ADDING_NEW_ACCOUNT, true);   // <-- this


    Bundle bundle = new Bundle();
    bundle.putParcelable(AccountManager.KEY_INTENT, intent);

    return bundle;
}

答案

克里斯·拉尔森

感谢您的回答。老实说,我认为我们使用 AccountManager 可能是错误的。

我们想在我们的应用程序之间共享一些凭据,所以我们有一个 Service 来保存自定义 帐户类型 。由于应用知道 帐户类型 并共享签名证书,因此它们可以访问 Account

当每个应用程序启动时,它们都会尝试获取 Account。如果 Account 不存在,他们会通过调用 AccountManager.addAccount(...).

在我们的 Service 中触发登录页面

登录(通过 Web 服务)成功后,我们将通过 AccountManager.addAccountExplicitly(...) 将其提供给其他应用程序。之后不设置结果,不会影响结果。

它如何影响 AccountManager?这种方法正确吗?

编辑:我将描述如何——在经历了很多痛苦之后——我为我们的应用程序实现了身份验证服务。

您说您有一个 Service 作为身份验证器。我假设您按照规定的方式执行此操作:在您的清单中声明一个引用您的 class 的服务,其中子 class 是 android.accounts.AbstractAccountAuthenticator。清单条目还引用了一个 XML 文件,其中包含声明您的帐户类型的 account-authenticator 标记,以及设置 | 使用的名称和图标。帐户页面。这些东西都记录在案了。

对于我们的应用程序,我希望最后登录的用户自动登录,直到他们 selected "Sign Out" 导航抽屉。为此,应用程序的用户名将保留在 SharedPreferences 中。

因此,如果 SharedPreferences 没有 用户名,应用会调用 AccountManager.newChooseAccountIntent() 来获取我们的自定义帐户类型,然后调用 startActivityForResult意图是 returned。如果用户 selects "Add new account",那么 addAccount() 将在身份验证器上调用,如下所述。

我们的认证 activity 不仅有 UI 用于 username/password 登录,还有密码重置和新试用帐户创建,所以有很多活动部分。当用户输入 username/password 并按下登录按钮时,该应用程序将针对我们的服务器进行身份验证。如果成功,应用会为该用户名调用 AccountManager.addAccountExplicitly()

AccountManager 选择帐户 activity 将在这一切完成后调用 onActivityResult()。如果我们得到 RESULT_OK,应用程序会保留下一次登录操作的用户名。

如果 SharedPreferences 有用户名,并且客户经理为其注册了一个帐户,我们不需要 select 任何东西,直接跳到下一阶段。

使用帐户 selected/known,现在应用程序可以进行身份​​验证。它调用 peekAuthToken() 来查看令牌是否已注册,如果已注册则调用 invalidateAuthToken()。这样做是为了当应用程序在客户经理上调用 getAuthToken() 时,它会强制客户经理在应用程序的身份验证器上调用 getAuthToken() 以通过服务器进行身份验证。由于服务器未 returning 令牌,我们使用的是虚拟令牌,这就是它们每次都无效的原因。

现在有趣的是,如果用户 select 的帐户已经注册,则不会通过身份验证,而如果他们 select 添加新帐户并该操作成功,新帐户将通过身份验证。所以请注意,如果您注意到登录新帐户会导致 两次 往返您的身份验证服务器,现在您知道原因了。 (我有一些逻辑在帐户上使用 setUserData() 来指示预验证,但看起来我忘了完成该功能。嗯。)


让我们从一些背景开始澄清事情。

您有一些 AbstractAccountAuthenticator 的子class。您已创建此 class 以响应其注册帐户类型的身份验证请求。关于此 class 需要牢记的是,平台 AccountManager 始终是您的应用程序将与之交互的组件;您的应用永远不会直接与此 class 交互。因此,从某种意义上说,AccountManager 是您的身份验证服务的客户端。身份验证器执行的工作将在后台线程中进行。

现在,身份验证服务必须完成的部分工作是与用户交互,询问用户帐户名、密码、指纹 ID、智能卡等。因此,假设您有一个 AuthenticatorActivity那个工作。通常你会为此 subclass android.accounts.AccountAuthenticatorActivity。这个activityclass真的没什么特别的。它唯一做的就是在开始时期望引用 AccountAuthenticatorResponse,然后在 activity 退出时调用该响应的 onResult() 方法。

我使用的应用程序与您的应用程序相似,因为没有来自身份验证服务的令牌 returned。我仍然实施 getAuthToken 有两个原因:

  • 与 Google 规定的工作流程保持一致。
  • 为了方便 return 一个真正的令牌 if/when 我们增强了我们的身份验证服务 return 一个令牌。

所以让我们跟随弹跳球来了解一切是如何组合在一起的。

  • 您的应用 activity 已实现 AccountManagerCallback 接口,以便从 AccountManager.
  • 获取异步消息
  • 让我们假设您的应用保留了一个用于身份验证的用户名。您的应用使用此用户名创建了一个 Account 对象。
  • 您的应用可以选择调用 AccountManager.getAccountsByType() 以确保该帐户存在。
  • 您的应用为帐户调用 AccountManager.getAuthToken()
  • AccountManager 发现您的身份验证器已针对该帐户类型注册并调用身份验证器的 getAuthToken 方法。
  • getAuthToken 方法将联系您的身份验证服务器并获得响应。
  • 如果成功,您的身份验证器 return 就是(虚拟)身份验证令牌。

如果登录失败,事情就变得有趣了。

  • 由于您的身份验证器不是 return 身份验证令牌,它必须 return 启动您的 AuthenticatorActivity 的意图,以便用户可以重新进行身份验证.

  • AccountManager 使用您的意图来启动您的 AuthenticatorActivity。作为请求的一部分,它将获得对稍后需要的 AccountAuthenticatorResponse 的引用。

  • 您的 AuthenticatorActivity 与用户交互,获得 username/password,联系您的服务器,获得响应。假设认证成功。
  • 您的 activity 将完成,调用 AccountAuthenticatorResponseonResult 方法。
  • AccountManager 从响应对象获取通知时将调用带有结果的回调方法。

因此我们可以回答您的问题:

Methods setAccountAuthenticatorResult(Bundle) and onResult(Bundle) are meant to notify the AbstractAccountAuthenticator about the outcome, but I have a project working without them, what are they for ?

更准确地说,它们是为了通知 AccountManager 结果。 你确定它在工作吗?您是否尝试过使用无效的 username/password 登录? AccountManager 将假定身份验证已被取消,除非它收到其他通知。

What is onRequestContinued() for ?

我没有确切的答案。我的猜测是,它是为了让 AccountManager 以某种方式与 UI 在身份验证方面发生的事情保持同步。

If an Intent is returned with key AccountManager.KEY_INTENT in addAccount implementation, the AbstractAccountAuthenticator will start the Intent. I have noticed that many developers add extras. Who gets them ?

答案是:是的。此意图将转到您创建的用于处理身份验证的 AuthenticatorActivity,因此您的 activity 执行身份验证所需的任何数据都需要与这些额外内容一起传递。

When addAccount is finished and the account created, should onActivityResult be called on the Activity that triggered it?

此用例是当您的应用调用 AccountManager.newChooseAccountIntent() 然后调用 startActivityForResult() 以产生意图时。然后 AccountManager 的 "choose account" activity 开始。

现在,由于 "add new account" 是 "choose account" UI 上的一个选项,AccountManager 将在该选项为 select编辑。然后,您的身份验证器 return 是 Bundle,如 AbstractAccountAuthenticator 文档中所述 AccountManager。在此之后 "choose account" activity 将完成并在您的 activity.

上调用 onActivityResult()

希望您开始看到 AccountManager 充当您的应用程序和身份验证器组件之间的代理人。为了更清楚地说明这一点,考虑一下您可以将这些组件打包成 其他应用程序 可以在不知道任何细节的情况下使用它们对您的服务进行身份验证。如果您创建了自己的自定义帐户类型,则可以直接从设置 | 调用您的身份验证。设备的帐户页面。这是一个很好的测试,可以查看您的 addAccount() 方法是否正确实施。

Methods setAccountAuthenticatorResult(Bundle) and onResult(Bundle) are meant to notify the AbstractAccountAuthenticator about the outcome, but I have a project working without them, what are they for ?

这些方法(以及 onError)是将结果传回给任何可能等待结果的人,例如查看 AccountManager.getAuthToken(...) 和许多其他允许您指定 AccountManagerCallback<Bundle> 的内容。这是您的结果将传递到的地方。 null 对回调有效,因此除非您围绕结果构建应用程序,否则它也可以正常工作。

AccountAuthenticatorResponse.onResult(Bundle)onError(...) 如果你实现 getAuthToken 就更重要刷新)。

What is onRequestContinued() for ?

可用信息很少(显示:none)。我能找到的唯一一次使用它是在 AccountManagerService 那里,它只是一个为了记录目的而增加的计数器。我认为它目前没有任何作用。

When addAccount is finished and the account created, should onActivityResult be called on the Activity that triggered it?

只要您使用 startActivityForResult(..)

就应该可以工作

If an Intent is returned with key AccountManager.KEY_INTENT in addAccount implementation, the AbstractAccountAuthenticator will start the Intent. I have noticed that many developers add extras. Who gets them ?

正如克里斯已经提到的,您可以 return 启动您的 Activity 的 Intent 并传递您可能需要的任何额外内容。


关于一个不相关的说明:你真的应该尽量不要以纯文本形式存储任何密码。尽管 AccountManager 说 setPassword() 它不受保护,因此不安全。