Android Authenticator
对用Android的Authenticator,官方的API Guides是似乎没有很好的介绍文档,Reference中也只是简单地介绍了一下。虽然sync-adapters中对于Authenticator有提及,但是对于其用法也没有一个完整的认识,所以对其进行一定的学习并且记录了下来。
为什么需要Account Manager
我们完全可以自己自己的账户管理机制,提交一个登录表单到服务器然后返回一个认证token。但是通常覆盖不到一些细节,如果用户在另外一客户端修改了密码怎么办;认证token的过期处理;单点登录的实现。这些东西实现起来还是挺麻烦的,但是如果Android的认证框架已经实现了这些功能,并且额外提供了用户数据同步等额外的功能,那还有理由不使用它吗?Stop Trying to Reinvent the Wheel.
实现类
Account相关的类都位于android.accounts
包下,以下是其中几个主要的类。
AccountManager
提供访问所有用户账户的注册中心。 不同的在线服务有不同的处理账户和认证的方式,所以AccountManager对不同的account types
使用可插拔的authenticator
的模块,由第三方提供的Authenticators
处理实际的账户认证细节。AccountManager还可以为应用生成
auth token
,所以应用不必直接处理密码。Auth tokens
通常被AccountManager
缓存并重复利用。AccountAuthenticator
上面提到的可插拔的authenticator
模块,处理特定的account types
。AccountManager
通过查找到合适的AccountAuthenticator,与其进行通信来实现对特定的account types
的所有操作。AccountAuthenticatorActivity
当需要验证用户的时候,由authenticator
调用,来进行“登录/创建 account”的Activity的基类。
调用流程
以添加账户为例
关键代码
|
|
|
|
|
|
|
|
|
|
时序图如下
- 客户端调用
AccountManager#addAccount
方法添加账户。 AccountManager#addAccount
方法中会调用AmsTask#start
并返回结果,AmsTask#start
的主要内容是调用doWork
方法。AmsTask#doWork
方法以AIDL方式调用AbstractAccountAuthenticator#addAccount
方法。AbstractAccountAuthenticator#addAccount
方法需要用户实现其逻辑。实现逻辑中需要告诉框架到哪个Activity进行具体的账户添加动作,因此需要返回一个Bundle包含以AccountManager.KEY_INTENT
为key的Intent,Intent中需包含以AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE
为key的AccountAuthenticatorResponse
。AccountManager#addAccount
方法中后记调用AmsTask#Response#onResult
方法AmsTask#Response#onResult
逻辑中会判断AbstractAccountAuthenticator#addAccount
返回的Bundle中后否包含AccountManager.KEY_INTENT
,如果包含则启动对应的Activity,所以#3中的实现方法必须设置AccountManager.KEY_INTENT
。- 跳转的Activity需要继承
AccountAuthenticatorActivity
,该类的实现逻辑中可以调用AccountManager#addAccountExplicitly
添加账户,AccountManager#setPassword
更新密码,AccountManager#setAuthToken
设置token等等。最终需要调用AccountAuthenticatorActivity#setAccountAuthenticatorResult
方法设置返回结果并调用AccountAuthenticatorActivity#finish
结束当前的Activity AccountAuthenticatorActivity#finish
方法会调用AccountAuthenticatorResponse#onResult
并传递上部设置的返回结果。此处的AccountAuthenticatorResponse
对象正式之前#3设置的AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE
。AccountAuthenticatorActivity
会在初始化的时候自动获取protected void onCreate(Bundle icicle) { super.onCreate(icicle); mAccountAuthenticatorResponse = getIntent().getParcelableExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE); if (mAccountAuthenticatorResponse != null) { mAccountAuthenticatorResponse.onRequestContinued(); } }
AccountAuthenticatorResponse
是之前AmsTask#Response
的包装类,其onResult
实现会调用AmsTask#Response#onResult
方法- 由于这次Activity返回的Bundle并没有包含
AccountManager.KEY_INTENT
,所以会调用AmsTask#set
方法 - 客户端在#1调用了
AccountManager#addAccount
会返回实现了AccountManagerFuture
接口的AmsTask
对象,调用getResult方法可以获取结果,但是线程会阻塞轮询,直至#9完成为止。
当然并不是所有的Account操作都需要启动Activiy进行辅助的。有些逻辑可以直接在AbstractAccountAuthenticator
子类方法中直接完成,然后返回一个结果。这些实现类的返回结果方式都是通过Bundle中设置AccountManager
中定义的特殊Key来是实现的,例如KEY_ACCOUNTS
,KEY_BOOLEAN_RESULT
等等。这些方法不启动的Activiy,所以AccountManager的实现中是调用的Future2Task
而不是AmsTask
。
实现
Authenticator
Authenticator需要继承AbstractAccountAuthenticator
。
###其中几个主要的方法
addAccount
添加账号,需要返回Intent跳转到AccountAuthenticatorActivity进行实际的登录confirmCredentials
确认账号信息,也需要Activity的辅助,可以在一些账户切换的场合使用updateCredentials
更新账号信息,也需要Activity的辅助,可以在密码变更,token失效的时候调用getAuthToken
获取用户token,实现逻辑可以参考官方文档对AccountManager#getAuthToken
的描述:如果之前已经生成了token并缓存,可以通过AccountManager#peekAuthToken
返回之前缓存的token,否则,如果通过AccountManager#getPassword
能够获取的用户密码,则直接申请token,否则,跳转到登陆页面。
getAuthToken有三个重载方法:public AccountManagerFuture<Bundle> getAuthToken (Account account, String authTokenType, Bundle options, Activity activity, AccountManagerCallback<Bundle> callback, Handler handler)
这个方法适用于应用在前台运行时调用,必要的情况下,会弹出请求用户输入证书
public AccountManagerFuture<Bundle> getAuthToken (Account account, String authTokenType, boolean notifyAuthFailure, AccountManagerCallback<Bundle> callback, Handler handler)
API level 14已经废弃,使用下面的方法
public AccountManagerFuture<Bundle> getAuthToken (Account account, String authTokenType, Bundle options, boolean notifyAuthFailure, AccountManagerCallback<Bundle> callback, Handler handler)
这个方法适用于应用在后台运行时调用,如果需要用户输入证书,可以发出一个通知(notification)。如果
notifyAuthFailure
设置为true,需要的情况下会发出一个会启动对应Intent的通知;如果设置为false,那么是由应用来处理返回的Intent。
一些上述方法中使用到的参数:
- accountType
用于标示账户的类型,通常一种类型绑定到一个AccountAuthenticator。可以多个app使用同一个accountType实现单点登陆。 - authTokenType
用于标示authToken的类型,同一账户在不同的app中获取的authToken可能拥有不同的scope,从而可以用authTokenType来标示该authToken的类型。
AccountAuthenticatorService
Authenticator的实现类最后会由AccountManager
以AIDL的方式调用。所以实现需要注册响应的Service。AbstractAccountAuthenticator
已经已经实现了getIBinder
方法,所以只需在onBind
调用该方法即可。
同时还需要在Manifest中注册:
其中meta-data引用的resource决定显示在Setting & Account中的内容
android:icon,android:label,android:smallIcon决定在Account中的显示
android:accountType是传入AccountAuthorization#addAccount的值
android:accountPreferences可以定义Setting & Account中偏好界面,从而对账户进行一些偏好设置