基于Android 4.2.2的Account Manager源代码分析学习:设备上帐号类型列表的获取

获取系统中注册的帐号类型列表是一个典型的用例。比如,在系统设置界面中选择“添加帐户”,这是,系统显示一个所有支持的帐户类型的列表界面(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开始吧。

AuthenticatorDescription数组

类AuthenticatorDescription定义了对authenticator的一个描述,包括类型、标签、图标等等,可以对等于应用程序中创建的authenticator描述文件中声明的内容。事实上,后者就是用来初始化一个AuthenticatorDescription的。

ChooseAccountActivity类持有一个AuthenticatorDescription类型的数组成员mAuthDescs,并且负责将其填满,具体操作在updateAuthDescriptions()方法中进行:

[java] view plaincopy
  1. public class ChooseAccountActivity extends PreferenceActivity {  
  2.     private AuthenticatorDescription[] mAuthDescs;  
  3.     ...  
  4.   
  5.     @Override  
  6.     protected void onCreate(Bundle icicle) {  
  7.         ...  
  8.         updateAuthDescriptions();  
  9.     }  
  10.     …  
  11. }  

updateAuthDescriptions()

这个方法中,请求AccountManager来获取系统中注册的全部帐户:
[java] view plaincopy
  1. mAuthDescs = AccountManager.get(this).getAuthenticatorTypes();  

而AccountManager则继续将这个任务交给系统服务AccountManagerService:
[java] view plaincopy
  1. public AuthenticatorDescription[] getAuthenticatorTypes() {  
  2.     try {  
  3.         return mService.getAuthenticatorTypes();  
  4.     } catch (RemoteException e) {  
  5.         // will never happen  
  6.         throw new RuntimeException(e);  
  7.     }  
  8. }  

AccountManagerService

跳过IPC的细节,最终,任务落到AccountManagerService头上。执行getAuthenticatorTypes()方法:
[java] view plaincopy
  1. public AuthenticatorDescription[] getAuthenticatorTypes() {  
  2.     ...  
  3.     final int userId = UserHandle.getCallingUserId();  
  4.     final long identityToken = clearCallingIdentity();  
  5.     try {  
  6.         Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>  
  7.                 authenticatorCollection = mAuthenticatorCache.getAllServices(userId);  
  8.         AuthenticatorDescription[] types =  
  9.                 new AuthenticatorDescription[authenticatorCollection.size()];  
  10.         int i = 0;  
  11.         for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator  
  12.                 : authenticatorCollection) {  
  13.             types[i] = authenticator.type;  
  14.             i++;  
  15.         }  
  16.         return types;  
  17.     } finally {  
  18.         restoreCallingIdentity(identityToken);  
  19.     }  
  20. }  

AccountAuthenticatorCache

这里面出现了一个AccountAuthenticatorCache类,看上去是某种缓冲结构,看它的类定义:
[java] view plaincopy
  1. class AccountAuthenticatorCache  
  2.         extends RegisteredServicesCache<AuthenticatorDescription>  
  3.         implements IAccountAuthenticatorCache {  

它扩展自RegisteredServicesCache。后者顾名思义,是一个系统中已注册的Service的缓冲区。

RegisteredServicesCache的静态内部类ServiceInfo<V>定义了对一个Service的描述:
[java] view plaincopy
  1. public static class ServiceInfo<V> {  
  2.     public final V type;  
  3.     public final ComponentName componentName;  
  4.     public final int uid;  
  5.   
  6.     ...  
  7. }  

在这里,实际的范型参数是AuthenticatorDescription。
在AccountManagerService.getAuthenticatorTypes()方法中,调用mAuthenticatorCache.getAllServices(userId)来返回一个相关的ServiceInfo集合。而这个getAllServices()方法实际是由父类RegisteredServicesCache实现的:
[java] view plaincopy
  1. public Collection<ServiceInfo<V>> getAllServices(int userId) {  
  2.     synchronized (mServicesLock) {  
  3.         // Find user and lazily populate cache  
  4.         final UserServices<V> user = findOrCreateUserLocked(userId);  
  5.         if (user.services == null) {  
  6.             generateServicesMap(userId);  
  7.         }  
  8.         return Collections.unmodifiableCollection(  
  9.                 new ArrayList<ServiceInfo<V>>(user.services.values()));  
  10.     }  
  11. }  

其中,generateServicesMap(int)方法扫描已经安装的包,根据给出的userId来生成一个我们需要的Service的Map:
[java] view plaincopy
  1. private void generateServicesMap(int userId) {  
  2.     ...  
  3.   
  4.     final PackageManager pm = mContext.getPackageManager();  
  5.     final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<ServiceInfo<V>>();  
  6.     final List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser(  
  7.             new Intent(mInterfaceName), PackageManager.GET_META_DATA, userId);  
  8.     for (ResolveInfo resolveInfo : resolveInfos) {  
  9.         try {  
  10.             ServiceInfo<V> info = parseServiceInfo(resolveInfo);  
  11.             ...  
  12.             serviceInfos.add(info);  
  13.         } catch (XmlPullParserException e) {  
  14.             Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);  
  15.         } catch (IOException e) {  
  16.             Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);  
  17.         }  
  18.     }  
  19.     ...  
  20. }  

这里,通过PackageManager.queryIntentServicesAsUser()方法查找系统中的Service,并由mInterfaceName限定查询条件,并且给出了PackageManager.GET_META_DATA标记。针对PackageManager的实现,这里不展开研究了。我们转而看看这个mInterfaceName的由来。通过对代码的追索,可以看到它从AccountAuthenticatorCache类的构造方法被初始化:
[java] view plaincopy
  1. public AccountAuthenticatorCache(Context context) {  
  2.     super(context, AccountManager.ACTION_AUTHENTICATOR_INTENT,  
  3.             AccountManager.AUTHENTICATOR_META_DATA_NAME,  
  4.             AccountManager.AUTHENTICATOR_ATTRIBUTES_NAME, sSerializer);  
  5. }  

这里,调用了父类的构造方法:
[java] view plaincopy
  1. public RegisteredServicesCache(Context context, String interfaceName, String metaDataName,  
  2.         String attributeName, XmlSerializerAndParser<V> serializerAndParser) {  
  3.     mContext = context;  
  4.     mInterfaceName = interfaceName;  
  5.     mMetaDataName = metaDataName;  
  6.     mAttributesName = attributeName;  
  7.     ...  
  8. }  

转而查看AccountManager的这三个常量:
[java] view plaincopy
  1. public static final String ACTION_AUTHENTICATOR_INTENT =  
  2.         "android.accounts.AccountAuthenticator";  
  3. public static final String AUTHENTICATOR_META_DATA_NAME =  
  4.         "android.accounts.AccountAuthenticator";  
  5. public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";  

这样,正好对应到我们在App的Manifest中对Service创建的intent filter名称、<meta-data>标签的名称以及authenticator描述文件的根属性。由此可以推断接下来要做的事情:
  1. 全部含有"android.accounts.AccountAuthenticator"行为的intent-filter限定的Service将被查询出来
  2. 将通过对Service的<meta-data>标签的读取找到authenticator描述文件
  3. 将通过解析描述文件来初始化一个Authenticator对象
回到generateServicesMap()方法,对pm.queryIntentServicesAsUser()的调用返回一个ResolveInfo对象列表。ResolveInfo类的描述信息是:
[java] view plaincopy
  1. /** 
  2.  * Information that is returned from resolving an intent 
  3.  * against an IntentFilter. This partially corresponds to 
  4.  * information collected from the AndroidManifest.xml's 
  5.  * <intent> tags. 
  6.  */  
  7. public class ResolveInfo implements Parcelable {  

接下来,调用私有方法parseServiceInfo(ResolveInfo)来逐个将ResolveInfo对象列表中的元素解析为ServiceInfo对象:

[java] view plaincopy
  1. private ServiceInfo<V> parseServiceInfo(ResolveInfo service)  
  2.         throws XmlPullParserException, IOException {  
  3.     android.content.pm.ServiceInfo si = service.serviceInfo;  
  4.     ComponentName componentName = new ComponentName(si.packageName, si.name);  
  5.   
  6.     PackageManager pm = mContext.getPackageManager();  
  7.   
  8.     XmlResourceParser parser = null;  
  9.     try {  
  10.         parser = si.loadXmlMetaData(pm, mMetaDataName);  
  11.         if (parser == null) {  
  12.             throw new XmlPullParserException("No " + mMetaDataName + " meta-data");  
  13.         }  
  14.   
  15.         AttributeSet attrs = Xml.asAttributeSet(parser);  
  16.   
  17.         int type;  
  18.         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT  
  19.                 && type != XmlPullParser.START_TAG) {  
  20.         }  
  21.   
  22.         String nodeName = parser.getName();  
  23.         if (!mAttributesName.equals(nodeName)) {  
  24.             throw new XmlPullParserException(  
  25.                     "Meta-data does not start with " + mAttributesName +  " tag");  
  26.         }  
  27.   
  28.         V v = parseServiceAttributes(pm.getResourcesForApplication(si.applicationInfo),  
  29.                 si.packageName, attrs);  
  30.         if (v == null) {  
  31.             return null;  
  32.         }  
  33.         final android.content.pm.ServiceInfo serviceInfo = service.serviceInfo;  
  34.         final ApplicationInfo applicationInfo = serviceInfo.applicationInfo;  
  35.         final int uid = applicationInfo.uid;  
  36.         return new ServiceInfo<V>(v, componentName, uid);  
  37.     } catch (NameNotFoundException e) {  
  38.         throw new XmlPullParserException(  
  39.                 "Unable to load resources for pacakge " + si.packageName);  
  40.     } finally {  
  41.         if (parser != null) parser.close();  
  42.     }  
  43. }  

通过这个方法的执行,将应用程序中定义的authenticator及其服务的信息全部解析到ServiceInfo对象中,其中
[java] view plaincopy
  1. V v = parseServiceAttributes(pm.getResourcesForApplication(si.applicationInfo),  
  2.         si.packageName, attrs);  

这个方法是抽象方法,在子类AccountAuthenticatorCache中实现:

[java] view plaincopy
  1. public AuthenticatorDescription parseServiceAttributes(Resources res,  
  2.         String packageName, AttributeSet attrs) {  
  3.     TypedArray sa = res.obtainAttributes(attrs,  
  4.             com.android.internal.R.styleable.AccountAuthenticator);  
  5.     try {  
  6.         final String accountType =  
  7.                 sa.getString(com.android.internal.R.styleable.AccountAuthenticator_accountType);  
  8.         final int labelId = sa.getResourceId(  
  9.                 com.android.internal.R.styleable.AccountAuthenticator_label, 0);  
  10.         final int iconId = sa.getResourceId(  
  11.                 com.android.internal.R.styleable.AccountAuthenticator_icon, 0);  
  12.         final int smallIconId = sa.getResourceId(  
  13.                 com.android.internal.R.styleable.AccountAuthenticator_smallIcon, 0);  
  14.         final int prefId = sa.getResourceId(  
  15.                 com.android.internal.R.styleable.AccountAuthenticator_accountPreferences, 0);  
  16.         final boolean customTokens = sa.getBoolean(  
  17.                 com.android.internal.R.styleable.AccountAuthenticator_customTokens, false);  
  18.         if (TextUtils.isEmpty(accountType)) {  
  19.             return null;  
  20.         }  
  21.         return new AuthenticatorDescription(accountType, packageName, labelId, iconId,  
  22.                 smallIconId, prefId, customTokens);  
  23.     } finally {  
  24.         sa.recycle();  
  25.     }  
  26. }  

这样,通过范型机制和多态机制,就将<meta-data>指向的XML文件的解析及其产出的类型的控制权交给了实现类。在这里,我们得到了想要的AuthenticatorDescription对象。

回到AccountManager

最后,我们生成的AuthenticatorDescription对象数组返回到AccountManager.getAuthenticatorTypes()方法中,这也是我们实质性请求获取帐户类型列表的起点。CheeseAccountActivity类将会把这个返回列表的内容展示给用户:
展开阅读全文

没有更多推荐了,返回首页