基于Android 4.2.2的Account Manager源代码分析学习:设备上帐号类型列表的获取
2015-08-14 15:43
567 查看
获取系统中注册的帐号类型列表是一个典型的用例。比如,在系统设置界面中选择“添加帐户”,这是,系统显示一个所有支持的帐户类型的列表界面(ChooseAccountActivity)供用户点选。另外,在Contacts/Calendar等应用程序中,也会向系统请求创建帐户或者现实帐户列表。背后的操作是统一由Android Framework提供的。应用程序只要将设置好的intent发送出去即可。
在研究如何获取帐户类型列表之前,简要的描述一下,应用程序如何将一个帐号注册到系统中。这个注册过程包含一下的要素:
扩展AbstractAuthenticator类。实质上,这是IAuthenticator接口的一个实现。
创建一个Service,并且具备以下设定:
持有一个authenticator实例
onBind()方法返回authenticator对应的IBinder
包含authenticator的XML描述文件
Manifest中对应的Service声明中包含处理"android.accounts.AccountAuthenticator"的intent filter,以及指向authenticator描述文件的<meta-data>标记
那就从ChooseAccountActivity开始吧。
ChooseAccountActivity类持有一个AuthenticatorDescription类型的数组成员mAuthDescs,并且负责将其填满,具体操作在updateAuthDescriptions()方法中进行:
[java] view
plaincopy
public class ChooseAccountActivity extends PreferenceActivity {
private AuthenticatorDescription[] mAuthDescs;
...
@Override
protected void onCreate(Bundle icicle) {
...
updateAuthDescriptions();
}
…
}
这个方法中,请求AccountManager来获取系统中注册的全部帐户:
[java] view
plaincopy
mAuthDescs = AccountManager.get(this).getAuthenticatorTypes();
而AccountManager则继续将这个任务交给系统服务AccountManagerService:
[java] view
plaincopy
public AuthenticatorDescription[] getAuthenticatorTypes() {
try {
return mService.getAuthenticatorTypes();
} catch (RemoteException e) {
// will never happen
throw new RuntimeException(e);
}
}
[java] view
plaincopy
public AuthenticatorDescription[] getAuthenticatorTypes() {
...
final int userId = UserHandle.getCallingUserId();
final long identityToken = clearCallingIdentity();
try {
Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
AuthenticatorDescription[] types =
new AuthenticatorDescription[authenticatorCollection.size()];
int i = 0;
for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
: authenticatorCollection) {
types[i] = authenticator.type;
i++;
}
return types;
} finally {
restoreCallingIdentity(identityToken);
}
}
[java] view
plaincopy
class AccountAuthenticatorCache
extends RegisteredServicesCache<AuthenticatorDescription>
implements IAccountAuthenticatorCache {
它扩展自RegisteredServicesCache。后者顾名思义,是一个系统中已注册的Service的缓冲区。
RegisteredServicesCache的静态内部类ServiceInfo<V>定义了对一个Service的描述:
[java] view
plaincopy
public static class ServiceInfo<V> {
public final V type;
public final ComponentName componentName;
public final int uid;
...
}
在这里,实际的范型参数是AuthenticatorDescription。
在AccountManagerService.getAuthenticatorTypes()方法中,调用mAuthenticatorCache.getAllServices(userId)来返回一个相关的ServiceInfo集合。而这个getAllServices()方法实际是由父类RegisteredServicesCache实现的:
[java] view
plaincopy
public Collection<ServiceInfo<V>> getAllServices(int userId) {
synchronized (mServicesLock) {
// Find user and lazily populate cache
final UserServices<V> user = findOrCreateUserLocked(userId);
if (user.services == null) {
generateServicesMap(userId);
}
return Collections.unmodifiableCollection(
new ArrayList<ServiceInfo<V>>(user.services.values()));
}
}
其中,generateServicesMap(int)方法扫描已经安装的包,根据给出的userId来生成一个我们需要的Service的Map:
[java] view
plaincopy
private void generateServicesMap(int userId) {
...
final PackageManager pm = mContext.getPackageManager();
final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<ServiceInfo<V>>();
final List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser(
new Intent(mInterfaceName), PackageManager.GET_META_DATA, userId);
for (ResolveInfo resolveInfo : resolveInfos) {
try {
ServiceInfo<V> info = parseServiceInfo(resolveInfo);
...
serviceInfos.add(info);
} catch (XmlPullParserException e) {
Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
} catch (IOException e) {
Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
}
}
...
}
这里,通过PackageManager.queryIntentServicesAsUser()方法查找系统中的Service,并由mInterfaceName限定查询条件,并且给出了PackageManager.GET_META_DATA标记。针对PackageManager的实现,这里不展开研究了。我们转而看看这个mInterfaceName的由来。通过对代码的追索,可以看到它从AccountAuthenticatorCache类的构造方法被初始化:
[java] view
plaincopy
public AccountAuthenticatorCache(Context context) {
super(context, AccountManager.ACTION_AUTHENTICATOR_INTENT,
AccountManager.AUTHENTICATOR_META_DATA_NAME,
AccountManager.AUTHENTICATOR_ATTRIBUTES_NAME, sSerializer);
}
这里,调用了父类的构造方法:
[java] view
plaincopy
public RegisteredServicesCache(Context context, String interfaceName, String metaDataName,
String attributeName, XmlSerializerAndParser<V> serializerAndParser) {
mContext = context;
mInterfaceName = interfaceName;
mMetaDataName = metaDataName;
mAttributesName = attributeName;
...
}
转而查看AccountManager的这三个常量:
[java] view
plaincopy
public static final String ACTION_AUTHENTICATOR_INTENT =
"android.accounts.AccountAuthenticator";
public static final String AUTHENTICATOR_META_DATA_NAME =
"android.accounts.AccountAuthenticator";
public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
这样,正好对应到我们在App的Manifest中对Service创建的intent filter名称、<meta-data>标签的名称以及authenticator描述文件的根属性。由此可以推断接下来要做的事情:
全部含有"android.accounts.AccountAuthenticator"行为的intent-filter限定的Service将被查询出来
将通过对Service的<meta-data>标签的读取找到authenticator描述文件
将通过解析描述文件来初始化一个Authenticator对象
回到generateServicesMap()方法,对pm.queryIntentServicesAsUser()的调用返回一个ResolveInfo对象列表。ResolveInfo类的描述信息是:
[java] view
plaincopy
/**
* Information that is returned from resolving an intent
* against an IntentFilter. This partially corresponds to
* information collected from the AndroidManifest.xml's
* <intent> tags.
*/
public class ResolveInfo implements Parcelable {
接下来,调用私有方法parseServiceInfo(ResolveInfo)来逐个将ResolveInfo对象列表中的元素解析为ServiceInfo对象:
[java] view
plaincopy
private ServiceInfo<V> parseServiceInfo(ResolveInfo service)
throws XmlPullParserException, IOException {
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, mMetaDataName);
if (parser == null) {
throw new XmlPullParserException("No " + mMetaDataName + " meta-data");
}
AttributeSet attrs = Xml.asAttributeSet(parser);
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.START_TAG) {
}
String nodeName = parser.getName();
if (!mAttributesName.equals(nodeName)) {
throw new XmlPullParserException(
"Meta-data does not start with " + mAttributesName + " tag");
}
V v = parseServiceAttributes(pm.getResourcesForApplication(si.applicationInfo),
si.packageName, attrs);
if (v == null) {
return null;
}
final android.content.pm.ServiceInfo serviceInfo = service.serviceInfo;
final ApplicationInfo applicationInfo = serviceInfo.applicationInfo;
final int uid = applicationInfo.uid;
return new ServiceInfo<V>(v, componentName, uid);
} catch (NameNotFoundException e) {
throw new XmlPullParserException(
"Unable to load resources for pacakge " + si.packageName);
} finally {
if (parser != null) parser.close();
}
}
通过这个方法的执行,将应用程序中定义的authenticator及其服务的信息全部解析到ServiceInfo对象中,其中
[java] view
plaincopy
V v = parseServiceAttributes(pm.getResourcesForApplication(si.applicationInfo),
si.packageName, attrs);
这个方法是抽象方法,在子类AccountAuthenticatorCache中实现:
[java] view
plaincopy
public AuthenticatorDescription parseServiceAttributes(Resources res,
String packageName, AttributeSet attrs) {
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AccountAuthenticator);
try {
final String accountType =
sa.getString(com.android.internal.R.styleable.AccountAuthenticator_accountType);
final int labelId = sa.getResourceId(
com.android.internal.R.styleable.AccountAuthenticator_label, 0);
final int iconId = sa.getResourceId(
com.android.internal.R.styleable.AccountAuthenticator_icon, 0);
final int smallIconId = sa.getResourceId(
com.android.internal.R.styleable.AccountAuthenticator_smallIcon, 0);
final int prefId = sa.getResourceId(
com.android.internal.R.styleable.AccountAuthenticator_accountPreferences, 0);
final boolean customTokens = sa.getBoolean(
com.android.internal.R.styleable.AccountAuthenticator_customTokens, false);
if (TextUtils.isEmpty(accountType)) {
return null;
}
return new AuthenticatorDescription(accountType, packageName, labelId, iconId,
smallIconId, prefId, customTokens);
} finally {
sa.recycle();
}
}
这样,通过范型机制和多态机制,就将<meta-data>指向的XML文件的解析及其产出的类型的控制权交给了实现类。在这里,我们得到了想要的AuthenticatorDescription对象。
在研究如何获取帐户类型列表之前,简要的描述一下,应用程序如何将一个帐号注册到系统中。这个注册过程包含一下的要素:
扩展AbstractAuthenticator类。实质上,这是IAuthenticator接口的一个实现。
创建一个Service,并且具备以下设定:
持有一个authenticator实例
onBind()方法返回authenticator对应的IBinder
包含authenticator的XML描述文件
Manifest中对应的Service声明中包含处理"android.accounts.AccountAuthenticator"的intent filter,以及指向authenticator描述文件的<meta-data>标记
那就从ChooseAccountActivity开始吧。
AuthenticatorDescription数组
类AuthenticatorDescription定义了对authenticator的一个描述,包括类型、标签、图标等等,可以对等于应用程序中创建的authenticator描述文件中声明的内容。事实上,后者就是用来初始化一个AuthenticatorDescription的。ChooseAccountActivity类持有一个AuthenticatorDescription类型的数组成员mAuthDescs,并且负责将其填满,具体操作在updateAuthDescriptions()方法中进行:
[java] view
plaincopy
public class ChooseAccountActivity extends PreferenceActivity {
private AuthenticatorDescription[] mAuthDescs;
...
@Override
protected void onCreate(Bundle icicle) {
...
updateAuthDescriptions();
}
…
}
updateAuthDescriptions()
这个方法中,请求AccountManager来获取系统中注册的全部帐户:[java] view
plaincopy
mAuthDescs = AccountManager.get(this).getAuthenticatorTypes();
而AccountManager则继续将这个任务交给系统服务AccountManagerService:
[java] view
plaincopy
public AuthenticatorDescription[] getAuthenticatorTypes() {
try {
return mService.getAuthenticatorTypes();
} catch (RemoteException e) {
// will never happen
throw new RuntimeException(e);
}
}
AccountManagerService
跳过IPC的细节,最终,任务落到AccountManagerService头上。执行getAuthenticatorTypes()方法:[java] view
plaincopy
public AuthenticatorDescription[] getAuthenticatorTypes() {
...
final int userId = UserHandle.getCallingUserId();
final long identityToken = clearCallingIdentity();
try {
Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
AuthenticatorDescription[] types =
new AuthenticatorDescription[authenticatorCollection.size()];
int i = 0;
for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
: authenticatorCollection) {
types[i] = authenticator.type;
i++;
}
return types;
} finally {
restoreCallingIdentity(identityToken);
}
}
AccountAuthenticatorCache
这里面出现了一个AccountAuthenticatorCache类,看上去是某种缓冲结构,看它的类定义:[java] view
plaincopy
class AccountAuthenticatorCache
extends RegisteredServicesCache<AuthenticatorDescription>
implements IAccountAuthenticatorCache {
它扩展自RegisteredServicesCache。后者顾名思义,是一个系统中已注册的Service的缓冲区。
RegisteredServicesCache的静态内部类ServiceInfo<V>定义了对一个Service的描述:
[java] view
plaincopy
public static class ServiceInfo<V> {
public final V type;
public final ComponentName componentName;
public final int uid;
...
}
在这里,实际的范型参数是AuthenticatorDescription。
在AccountManagerService.getAuthenticatorTypes()方法中,调用mAuthenticatorCache.getAllServices(userId)来返回一个相关的ServiceInfo集合。而这个getAllServices()方法实际是由父类RegisteredServicesCache实现的:
[java] view
plaincopy
public Collection<ServiceInfo<V>> getAllServices(int userId) {
synchronized (mServicesLock) {
// Find user and lazily populate cache
final UserServices<V> user = findOrCreateUserLocked(userId);
if (user.services == null) {
generateServicesMap(userId);
}
return Collections.unmodifiableCollection(
new ArrayList<ServiceInfo<V>>(user.services.values()));
}
}
其中,generateServicesMap(int)方法扫描已经安装的包,根据给出的userId来生成一个我们需要的Service的Map:
[java] view
plaincopy
private void generateServicesMap(int userId) {
...
final PackageManager pm = mContext.getPackageManager();
final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<ServiceInfo<V>>();
final List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser(
new Intent(mInterfaceName), PackageManager.GET_META_DATA, userId);
for (ResolveInfo resolveInfo : resolveInfos) {
try {
ServiceInfo<V> info = parseServiceInfo(resolveInfo);
...
serviceInfos.add(info);
} catch (XmlPullParserException e) {
Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
} catch (IOException e) {
Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
}
}
...
}
这里,通过PackageManager.queryIntentServicesAsUser()方法查找系统中的Service,并由mInterfaceName限定查询条件,并且给出了PackageManager.GET_META_DATA标记。针对PackageManager的实现,这里不展开研究了。我们转而看看这个mInterfaceName的由来。通过对代码的追索,可以看到它从AccountAuthenticatorCache类的构造方法被初始化:
[java] view
plaincopy
public AccountAuthenticatorCache(Context context) {
super(context, AccountManager.ACTION_AUTHENTICATOR_INTENT,
AccountManager.AUTHENTICATOR_META_DATA_NAME,
AccountManager.AUTHENTICATOR_ATTRIBUTES_NAME, sSerializer);
}
这里,调用了父类的构造方法:
[java] view
plaincopy
public RegisteredServicesCache(Context context, String interfaceName, String metaDataName,
String attributeName, XmlSerializerAndParser<V> serializerAndParser) {
mContext = context;
mInterfaceName = interfaceName;
mMetaDataName = metaDataName;
mAttributesName = attributeName;
...
}
转而查看AccountManager的这三个常量:
[java] view
plaincopy
public static final String ACTION_AUTHENTICATOR_INTENT =
"android.accounts.AccountAuthenticator";
public static final String AUTHENTICATOR_META_DATA_NAME =
"android.accounts.AccountAuthenticator";
public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
这样,正好对应到我们在App的Manifest中对Service创建的intent filter名称、<meta-data>标签的名称以及authenticator描述文件的根属性。由此可以推断接下来要做的事情:
全部含有"android.accounts.AccountAuthenticator"行为的intent-filter限定的Service将被查询出来
将通过对Service的<meta-data>标签的读取找到authenticator描述文件
将通过解析描述文件来初始化一个Authenticator对象
回到generateServicesMap()方法,对pm.queryIntentServicesAsUser()的调用返回一个ResolveInfo对象列表。ResolveInfo类的描述信息是:
[java] view
plaincopy
/**
* Information that is returned from resolving an intent
* against an IntentFilter. This partially corresponds to
* information collected from the AndroidManifest.xml's
* <intent> tags.
*/
public class ResolveInfo implements Parcelable {
接下来,调用私有方法parseServiceInfo(ResolveInfo)来逐个将ResolveInfo对象列表中的元素解析为ServiceInfo对象:
[java] view
plaincopy
private ServiceInfo<V> parseServiceInfo(ResolveInfo service)
throws XmlPullParserException, IOException {
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, mMetaDataName);
if (parser == null) {
throw new XmlPullParserException("No " + mMetaDataName + " meta-data");
}
AttributeSet attrs = Xml.asAttributeSet(parser);
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.START_TAG) {
}
String nodeName = parser.getName();
if (!mAttributesName.equals(nodeName)) {
throw new XmlPullParserException(
"Meta-data does not start with " + mAttributesName + " tag");
}
V v = parseServiceAttributes(pm.getResourcesForApplication(si.applicationInfo),
si.packageName, attrs);
if (v == null) {
return null;
}
final android.content.pm.ServiceInfo serviceInfo = service.serviceInfo;
final ApplicationInfo applicationInfo = serviceInfo.applicationInfo;
final int uid = applicationInfo.uid;
return new ServiceInfo<V>(v, componentName, uid);
} catch (NameNotFoundException e) {
throw new XmlPullParserException(
"Unable to load resources for pacakge " + si.packageName);
} finally {
if (parser != null) parser.close();
}
}
通过这个方法的执行,将应用程序中定义的authenticator及其服务的信息全部解析到ServiceInfo对象中,其中
[java] view
plaincopy
V v = parseServiceAttributes(pm.getResourcesForApplication(si.applicationInfo),
si.packageName, attrs);
这个方法是抽象方法,在子类AccountAuthenticatorCache中实现:
[java] view
plaincopy
public AuthenticatorDescription parseServiceAttributes(Resources res,
String packageName, AttributeSet attrs) {
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AccountAuthenticator);
try {
final String accountType =
sa.getString(com.android.internal.R.styleable.AccountAuthenticator_accountType);
final int labelId = sa.getResourceId(
com.android.internal.R.styleable.AccountAuthenticator_label, 0);
final int iconId = sa.getResourceId(
com.android.internal.R.styleable.AccountAuthenticator_icon, 0);
final int smallIconId = sa.getResourceId(
com.android.internal.R.styleable.AccountAuthenticator_smallIcon, 0);
final int prefId = sa.getResourceId(
com.android.internal.R.styleable.AccountAuthenticator_accountPreferences, 0);
final boolean customTokens = sa.getBoolean(
com.android.internal.R.styleable.AccountAuthenticator_customTokens, false);
if (TextUtils.isEmpty(accountType)) {
return null;
}
return new AuthenticatorDescription(accountType, packageName, labelId, iconId,
smallIconId, prefId, customTokens);
} finally {
sa.recycle();
}
}
这样,通过范型机制和多态机制,就将<meta-data>指向的XML文件的解析及其产出的类型的控制权交给了实现类。在这里,我们得到了想要的AuthenticatorDescription对象。
回到AccountManager
最后,我们生成的AuthenticatorDescription对象数组返回到AccountManager.getAuthenticatorTypes()方法中,这也是我们实质性请求获取帐户类型列表的起点。CheeseAccountActivity类将会把这个返回列表的内容展示给用户:相关文章推荐
- 用imageswitcher和gallery实现壁纸切换效果
- Android内存溢出处理方案
- Android开发IDE配置及选择
- Android双击,连续多次点击事件判断
- Android应用开发原理之从源码分析看Linearlayout、Relativelayout,Framelayout的布局差别(Relativelayout分析)
- Android API的Activity类
- Android 中比 Timer 更好方法
- Activity对象的onCreate方法真是Android程序的入口吗?
- Android事件分发机制完全解析,带你从源码的角度彻底理解(下)
- Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
- Android中的帧动画与补间动画的使用
- android给子线程传值
- Android训练课程(Android Training) - 测试你的Android Activity
- Android Studio 学习笔记(1)
- Android屏幕适配总结
- Android指南 - 主题
- Android指南 - 样式和主题
- Android训练课程(Android Training) - 构建你的第一个应用
- Android训练课程(Android Training) - 添加活动栏(使用action bar)
- Android设计 - 图标设计概述(Iconography)