10.5.2 安装Content Provider
安装Content Provider由ActivityThread的installSystemProviders方法完成。这里要安装的便是上文中查询的SettingsProvider。installSystemProviders的代码如下:
public final class ActivityThread{
……
public final void installSystemProviders(List<ProviderInfo>providers){
if(providers!=null){
installContentProviders(mInitialApplication, providers);
}
}
installSystemProviders方法将请求转发给installContentProviders方法,installContent Providers是安装Content Provider的通用接口。针对SettingsProvider,需要接收参数mInitialApplication,该参数是在ActivityThread.systemMain阶段由Instrumentation创建的Application。
installSystemProviders方法的代码如下:
public final class ActivityThread{
……
private void installContentProviders(
Context context, List<ProviderInfo>providers){
final ArrayList<IActivityManager.ContentProviderHolder>results=
new ArrayList<IActivityManager.ContentProviderHolder>();
for(ProviderInfo cpi:providers){
//调用installProvider,返回ContentProviderHolder
IActivityManager.ContentProviderHolder cph=
installProvider(context, null, cpi,
false/noisy/,true/noReleaseNeeded/,true/stable/);
if(cph!=null){
cph.noReleaseNeeded=true;
results.add(cph);//存入results中,用于发布
}
}
//向ActivityManagerService发布该Content Provider
try{
ActivityManagerNative.getDefault().publishContentProviders(
getApplicationThread(),results);
}catch(RemoteException ex){
}
}
installContentProviders是安装Content Provider的通用接口,其主要执行以下两步操作:
1)调用installProvider方法创建Content Provider,存入ContentProviderHolder中。
2)调用publishContentProviders方法向ActivityManagerService发布该Content Provider。
1.调用installProvider方法创建Content Provider
接下来分析ActivityThread的installProvider方法如何创建Content Provider,代码如下:
//参数分别为context, null, cpi, false, true, true
private IActivityManager.ContentProviderHolder installProvider(
Context context, IActivityManager.ContentProviderHolder holder,
ProviderInfo info, boolean noisy,
boolean noReleaseNeeded, boolean stable){
ContentProvider localProvider=null;
IContentProvider provider;
if(holder==null||holder.provider==null){//holder参数为null
Context c=null;
ApplicationInfo ai=info.applicationInfo;
/*context即mInitialApplication,表示frameworks-res.apk
ai是SettingProvider对应的应用信息,这里不相等/
if(context.getPackageName().equals(ai.packageName)){
c=context;
}else if(mInitialApplication!=null&&
mInitialApplication.getPackageName().equals(ai.packageName)){
c=mInitialApplication;
}else{//执行该分支
try{
//创建SettingsProvider对应的Context
c=context.createPackageContext(ai.packageName,
Context.CONTEXT_INCLUDE_CODE);
}catch(PackageManager.NameNotFoundException e){
//忽略
}
}
if(c==null){
return null;//Context必须创建成功,否则直接返回
}
try{
//通过Context获取类加载器
final java.lang.ClassLoader cl=c.getClassLoader();
//通过Java反射机制在本进程创建SettingsProvider的实例
localProvider=(ContentProvider)cl.loadClass(info.name).newInstance();
/*返回Content Provider的mTransport成员变量,Content Provider需要发布到
ActivityManagerService,因此需要一个Binder服务接口/
provider=localProvider.getIContentProvider();
//Binder服务接口不能为null,否则无法发布,其他组件也无法使用
if(provider==null){
return null;
}
/*设置Content Provider的权限并调用其子类的onCreate
方法,本例中即调用SettingsProvider的onCreate方法/
localProvider.attachInfo(c, info);
}catch(java.lang.Exception e){
return null;
}
}else{//对应最外层if
provider=holder.provider;
}
IActivityManager.ContentProviderHolder retHolder;
synchronized(mProviderMap){
IBinder jBinder=provider.asBinder();
if(localProvider!=null){
//查询是否已经发布过该Content Provider
ComponentName cname=new ComponentName(info.packageName, info.name);
ProviderClientRecord pr=mLocalProvidersByName.get(cname);
if(pr!=null){
provider=pr.mProvider;//使用已有的
}else{
/*以ProviderInfo为参数构造holder, ProviderInfo存储了ContentProvider
的权限和AndroidManifest.xml中android:authorities属性值/
holder=new IActivityManager.ContentProviderHolder(info);
//holder存储Provider的Binder服务接口
holder.provider=provider;
holder.noReleaseNeeded=true;
/*创建ProviderClientRecord,并将其存入mProviderMap中。android:authorities
属性值以“;”分割,这里以每个authority为键存储ProviderClientRecord/
pr=installProviderAuthoritiesLocked(provider, localProvider, holder);
//以Binder为键存储ProviderClientRecord
mLocalProviders.put(jBinder, pr);
//以组件名为键存储ProviderClientRecord
mLocalProvidersByName.put(cname, pr);
}
retHolder=pr.mHolder;
}else{//对应localProvider为null,本例中不为null
//获取该ContentProvider的引用计数
ProviderRefCount prc=mProviderRefCountMap.get(jBinder);
if(prc!=null){
//更新引用计数
if(!noReleaseNeeded){
incProviderRefLocked(prc, stable);
try{
ActivityManagerNative.getDefault().removeContentProvider(
holder.connection, stable);
}catch(RemoteException e){
//do nothing content provider object is dead any way
}
}
}else{//对应prc为null,添加新的引用计数
ProviderClientRecord client=
installProviderAuthoritiesLocked(
provider, localProvider, holder);
if(noReleaseNeeded){
prc=new ProviderRefCount(holder, client,1000,1000);
}else{
prc=stable
?new ProviderRefCount(holder, client,1,0)
:new ProviderRefCount(holder, client,0,1);
}
mProviderRefCountMap.put(jBinder, prc);
}
retHolder=prc.holder;
}
}
return retHolder;
}
installProvider的执行过程比较复杂,需要结合其类图关系分析,如图10-6所示。
图 10-6 installProvider的类图关系
从上图可以归纳如下信息:
SettingsProvider继承自ContentProvider,用于提供数据共享接口,其覆盖了父类的onCreate方法。
ContentProvider的mContext成员变量用于描述当前Content Provider的运行环境,mTransact成员变量用于描述当前Content Provider的Binder服务接口。
mTransact类型是Transact,该类相当于Binder体系结构中的工具类,用于提供远程访问功能。Content Provider需要将mTransact发布给ActivityManagerService,以便使用Content Provider的Client组件可以向ActivityManagerService查询所需要的Content Provider, ActivityManagerService根据mTransact返回对应的ContentProviderProxy给Client。
mTransact最终存储到ContentProviderHolder的provider成员变量中,Content Provider Holder是Parcelable的子类,可以跨进程传输。因此当传入ActivityThread.install方法的holder参数为null时,说明要安装的Content Provider运行于当前进程,需要通过反射机制加载并创建具体的Content Provider对象;而当holder参数不为null时,说明要安装的Content Provider来自于其他进程,此时只需要从holder中取出Content Provider并更新或增加其引用计数即可。
ActivityThread定义了内部类ProviderClient Record,用于存储具体的ContentProvider。其mProvider成员变量存储的是本进程运行的Content Provider的引用,其成员变量mLocalProvider存储的是本进程运行的Content Provider的Binder服务接口,其成员变量mNames存储的是android:authorities配置信息。
ActivityThread的成员变量mProviderMap,以Content Provider的authority为键存储Provider Client Record,成员变量mLocalProviders以Binder引用为键存储ProviderClientRecord,成员变量mLocalProvidersByName以组件名为键存储ProviderClientRecord。
2.调用publishContentProviders方法发布Content Provider
Content Provider的发布过程最终由ActivityManagerService的publishContentProviders方法完成,其代码如下:
public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder>providers){
……
synchronized(this){
//获取调用方进程的ProcessRecord
final ProcessRecord r=getRecordForAppLocked(caller);
……
final long origId=Binder.clearCallingIdentity();
/*遍历传入的列表,该列表的元素是ContentProviderHolder,其中
一个元素就存储了本例分析的SettingsProvider/
final int N=providers.size();
for(int i=0;i<N;i++){
ContentProviderHolder src=providers.get(i);
……
//从调用进程中获取ContentProviderRecord
ContentProviderRecord dst=r.pubProviders.get(src.info.name);
if(dst!=null){
ComponentName comp=new ComponentName(dst.info.packageName,
dst.info.name);
/*ActivityManagerService内部通过mProviderMap以组件名为键
保存Content Provider信息/
mProviderMap.putProviderByClass(comp, dst);
String names[]=dst.info.authority.split(";");
/同时以authority为键保存Content Provider信息/
for(int j=0;j<names.length;j++){
mProviderMap.putProviderByName(names[j],dst);
}
//等待启动的Content Provider,一旦启动并发布,将从该列表删除
int NL=mLaunchingProviders.size();
int j;
for(j=0;j<NL;j++){
if(mLaunchingProviders.get(j)==dst){
mLaunchingProviders.remove(j);
j—;
NL—;
}
}
synchronized(dst){
dst.provider=src.provider;
//ContentProviderRecord需要关联ProcessRecord
dst.proc=r;
//通知正在等待该Content Provider启动的Client
dst.notifyAll();
}
//发布Content Provider后,需要调整进程的OOM adj值
updateOomAdjLocked(r);
}
}
Binder.restoreCallingIdentity(origId);
}
}
publishContentProviders的工作流程很简单,其流程如下:
1)根据调用者传入的IApplicationThread参数,获取调用进程的ProcessRecord。
2)将Content Provider信息存入ActivityManagerService的成员变量mProviderMap中。存储方法有两种,即对应两种检索方法。
3)mLaunchingProviders中存储了Client请求使用的Content Provider信息。当前Content Provider已启动,因此需要从mLaunchingProviders中移除该Content Provider的信息。
4)将Content Provider与其ProcessRecord关联。
5)通知Client,其请求使用的Content Provider已发布。
以上步骤完成后,ActivityManagerService便进入第四阶段。