创建自定义帐号类型

到目前为止,我们已经讨论了如何访问 Google API,它们使用由 Google 定义的帐号和用户。如果您拥有自己的在线服务,它将没有 Google 帐号或用户,这种情况下您该怎么办呢?事实证明,在用户的设备上安装新的帐号类型还是比较简单的。本课程介绍了如何创建与内置帐号工作原理相同的自定义帐号类型。

实现您的自定义帐号代码

首先,您需要使用一种方式从用户那里获取凭据:既可以使用简单的方式,如要求输入用户名和密码的对话框;也可以使用更为独特的流程,比如动态密码或生物识别扫描。无论采用哪种方式,您都需要实现具有以下功能的代码:

  1. 从用户那里收集凭据
  2. 通过服务器验证凭据
  3. 在设备上存储凭据

通常情况下,这三项要求全都可以由一个 Activity 进行处理。我们称之为身份验证器 Activity。

由于身份验证器 Activity 需要与 AccountManager 系统互动,因此,它们具有普通 Activity 所没有的某些要求。为了简化这一流程,Android 框架提供了一个基类 AccountAuthenticatorActivity,您可以扩展该基类以创建自己的自定义身份验证器。

如何处理身份验证器 Activity 的前两项要求(即凭据收集和身份验证)完全取决于您自己(毕竟,如果只有一种处理方式,那就用不上“自定义”帐号类型了)。第三项要求有一个规范且相当简单的实现:

Kotlin

    Account(username, your_account_type).also { account ->
        accountManager.addAccountExplicitly(account, password, null)
    }
    

Java

    final Account account = new Account(username, your_account_type);
    accountManager.addAccountExplicitly(account, password, null);
    

清楚了解安全注意事项!

请务必注意,AccountManager 并非加密服务或密钥链。它会以明文形式按原样存储您传递的帐号凭据。在大多数设备上,这个问题并不需要特别关注,因为它会将这些凭据存储在只有 root 身份才能访问的数据库中。但在已取得 root 权限的设备上,可使用 adb 访问设备的任何人均可读取这些凭据。

考虑到这一点,您不应将用户的实际密码传递给 AccountManager.addAccountExplicitly(),而是应该存储对攻击者用处有限的加密安全令牌。如果您的用户凭据保护着一些有价值的内容,那么您应该仔细考虑执行类似操作。

注意:对于安全方面的代码,请遵循“Mythbusters”规则:请勿在家中尝试(避免自己编写此类代码)!在实现任何自定义帐号代码之前,请咨询安全专家。

现在,在详细说明安全事项后,是时候继续执行后续工作了。您已经实现了自定义帐号代码的主要内容;剩下的便是穿针引线了。

扩展 AbstractAccountAuthenticator

为了让 AccountManager 使用您的自定义帐号代码,您需要一个类来实现 AccountManager 所期望的接口。这个类便是身份验证器类。

要创建身份验证器类,最简单的方式就是扩展 AbstractAccountAuthenticator 并实现其抽象方法。如果您已经完成之前的课程,则应该熟悉 AbstractAccountAuthenticator 的抽象方法:它们与您在上一节课中为获取帐号信息和授权令牌而调用的方法相反。

正确实现身份验证器类需要一些单独的代码段。首先,AbstractAccountAuthenticator 有七个必须替换的抽象方法。其次,您需要将适用于 "android.accounts.AccountAuthenticator"Intent 过滤器添加到应用清单中(如下一部分所示)。最后,您必须提供两个 XML 资源,它们需要定义自定义帐号类型的名称以及系统将在此类型的帐号旁显示的图标等内容。

您可以在 AbstractAccountAuthenticator 文档中找到成功实现身份验证器类和 XML 文件的分步指南。该文档还通过 SampleSyncAdapter 示例应用提供了一个示例实现。

在通读 SampleSyncAdapter 代码时,您会注意到,有些方法在 bundle 中返回了某个 Intent。该 Intent 就是将用于启动自定义身份验证器 Activity 的 Intent。如果您的身份验证器 Activity 需要任何特殊的初始化参数,您可以使用 Intent.putExtra() 将它们附加到该 Intent。

创建身份验证器服务

现在,您已经有了一个身份验证器类,还需要一个位置使其生效。帐号身份验证器需要用于多个应用并在后台运行,因此它们需要在 Service 内运行。我们将这称为身份验证器服务。

身份验证器服务可以很简单。它只需在 onCreate() 中创建一个身份验证器类的实例,并在 onBind() 中调用 getIBinder()SampleSyncAdapter 中包含一个很好的身份验证器服务的示例。

请务必将 <service> 标记添加到您的清单文件中,然后为 AccountAuthenticator Intent 添加一个 Intent 过滤器,并声明帐号身份验证器:

    <service ...>
       <intent-filter>
          <action android:name="android.accounts.AccountAuthenticator" />
       </intent-filter>
       <meta-data android:name="android.accounts.AccountAuthenticator"
                 android:resource="@xml/authenticator" />
    </service>
    

分发服务

大功告成!系统现在可以识别您的帐号类型,以及所有众所周知的帐号类型,例如“Google”和“Corporate”。您可以通过帐号和同步设置页面添加帐号,而需要自定义类型帐号的应用将能够执行枚举和身份验证操作,就像使用其他任何帐号类型时一样。

当然,所有这些都以假定设备上实际安装了您的帐号服务为基础。如果只有一个应用访问该服务,那么这不是什么大问题,只需在应用中捆绑服务即可。不过,如果您希望多个应用使用您的帐号服务,则事情会变得更棘手一些。您不会希望将该服务与所有应用绑定在一起,也不会希望有多个会占用用户设备空间的副本。

一种解决方案是将该服务安置在一个小的专用 APK 中。如果应用想使用您的自定义帐号类型,它会检查设备,看您的自定义帐号服务是否可用。如果不可用,则它会将用户引导至 Google Play 以下载该服务。这一开始似乎有些麻烦,但与每个使用您的自定义帐号的应用都需要重新输入凭据这一替代方案相比,它更加简单易用。