AccountManager帐号管理器,集中管理apps注册的不同类型的帐号。
不同类型的帐号服务会使用不同的帐号登录和鉴权方式,所以AccountManager为不同类型的帐号提供一个插件式authenticator模块,authenticators自己处理帐号登录/认证的具体细节,也可以自己存储帐号信息
简言之,AccountManager是一个面向应用程序开发的组件,它提供了一套对应于IAccountManager协议的应用程序接口;这组接口通过Binder机制与系统服务AccountManagerService进行通信,协作完成帐号相关的操作。同时,AccountManager接收authenticators提供的回调,以便在帐号操作完成之后向调用此帐号服务的业务返回对应的接口,同时触发这个业务对结果的处理。
- authenticators 即注册帐号服务的app;
- 业务调用方 即使用authenticators提供的帐号服务的第三方,也可以是authenticator自己
如果应用想要注册一个新的帐号服务,必须实现AbstractAccountAuthenticator类,这是创建一个account authenticator的抽象基础类;然后新建一个authenticator service,注册action必须为”android.accounts.AccountAuthenticator”,且该service要实现onBinder(android.content.Intent)方法,返回AbstractAccountAuthenticator实现类的实例
说下必须要注册一个action为”android.accounts.AccountAuthenticator”的authenticator service:
首先,AbstractAccountAuthenticator是创建一个account authenticator必须实现的抽象基础类,接口协议定义在IAccountAuthenticator中,是一个authenticator自定义自己登录/认证等的接口协议;
那如何将authenticator的实现回调给AccountManagerService,供其调起authenticator的具体实现呢?
就是通过action注册为”android.accounts.AccountAuthenticator”的authenticator service了:
这个action即为AccountManager#ACTION_AUTHENTICATOR_INTENT的常量值,系统服务AccountManagerService是通过bind到action为AccountManager#ACTION_AUTHENTICATOR_INTENT的intent service上来调起某个账号类型的authenticator service,然后通过调用这个service的getBinder()方法来获取AbstractAccountAuthenticator的实现实例,进而调用authenticator对帐号登录认证等服务的具体实现
至于每个帐号服务都定义一个action为”android.accounts.AccountAuthenticator”的service,那AccountManagerService是如何区分的呢?
当然是通过账号类型了,每个accountType只能对应一个authenticator
那系统是如何知道每个authenticator service对应的账号类型?
在AndroidManifest.xml中注册authenticator service时声明帐号属性的meta-data配置,声明的meta-data是一个name为 “android.accounts.AccountAuthenticator”的xml 资源(AccountManager#AUTHENTICATOR_META_DATA_NAME),该XML资源文件定义了account-authenticator用到的一些属性:如accountType;系统解析authenticator service info之后,loadXmlMetaData获取authenticator 的xml属性,然后利用 Xml.asAttributeSet即
final PackageManager pm = mContext.getPackageManager();
final List resolveInfos = pm.queryIntentServicesAsUser(new Intent("android.accounts.AccountAuthenticator", PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AWARE| PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
for (ResolveInfo resolveInfo : resolveInfos) {
android.content.pm.ServiceInfo si = service.serviceInfo;
ComponentName componentName = new ComponentName(si.packageName, si.name);
PackageManager pm = mContext.getPackageManager();
XmlResourceParser parser = null;
try {
parser = si.loadXmlMetaData(pm, "android.accounts.AccountAuthenticator")
if (parser == null) {
throw new XmlPullParserException("No " + mMetaDataName + " meta-data");
}
AttributeSet attrs = Xml.asAttributeSet(parser);
...//解析authenticator xml的帐号属性
}
...
}
创建一个继承自AbstractAccountAuthenticator的类TestAccountAuthenticator
public class TestAccountAuthenticator extends AbstractAccountAuthenticator {
private Context mContext;
public TestAccountAuthenticator(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 {//登录界面的定制化实现
Intent addAccountIntent = new Intent(mContext, LoginActivity.class);
addAccountIntent.putExtra("authTokenType", authTokenType);
if (options != null) {
addAccountIntent.putExtras(options);
}
addAccountIntent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);//一定要把response传入intent的extra中,便于将登录操作的结果回调给AccountManager
Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, addAccountIntent);
return bundle;
}
@Override
public Bundle getAccountRemovalAllowed(AccountAuthenticatorResponse response, Account account) throws NetworkErrorException {//是否允许删除你的账号,这里是不允许删除,可自定义什么时候可以被删除,默认是true
Bundle bundle = new Bundle();
bundle.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
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 {//自己完成获取鉴权token的流程
return null;
}
@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;
}
}
创建一个authenticator service—TestAuthenticatiorService,实现onBinder()方法,在onBinder方法里返回TestAccountAuthentcator的实例
public class TestAuthenticatiorService extends Service {
private static final String TAG = "XmAuthenticationService";
private TestAccountAuthenticator mAuthenticator;
@Override
public void onCreate() {
super.onCreate();
mAuthenticator = new TestAccountAuthenticator(this);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mAuthenticator.getIBinder();
}
}
在AndroidManifest.xml文件中注册该TestAuthenticatorService
<service
android:name=".TestAuthenticatiorService"
android:exported="true">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
service>
其中,authenticator是一个xml的资源文件,定义了account的一些属性
<account-authenticator
xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.test"//账号类型
android:icon="@drawable/icon"//设置-同步-添加 账号类型的icon
android:smallIcon="@drawable/miniIcon"//小icon
android:label="@string/label"//设置-同步-添加 账号类型的名称
android:accountPreferences="@xml/account_preferences"//在设置中展示的一些偏好
android:customTokens="false"//authenticator是否要自己处理auth token的存储和获取权限
/>
ps:说下customTokens属性
如设置为true,就需要在TestAccountAuthenticator类的getAuthToken方法的实现中自己进行caller app的权限检查和token存储问题
如不设置(默认为false)或设置为false,则是使用AccountManager的权限检查和存储机制,默认只有签名相同的app才可调用getAuthToken()方法,存储在系统数据库中,但要app判断是否有效,失效要调用invalidate才可清除系统的存储
到这里,你就成功注册了一个新的帐号类型了