码迷,mamicode.com
首页 > 其他好文 > 详细

ContentProvider使用拾遗

时间:2016-05-13 02:51:51      阅读:113      评论:0      收藏:0      [点我收藏+]

标签:

一、ContentProvider所在应用的启动时机

ContentProvider可向多个进程提供数据,比如ContentProvider实现在应用A中,A的进程尚未启动,另一个应用(进程)B在使用ContentProvider时就会启动应用A。

在调用ContentResolver的query时,会先获取A中ContentProvider在进程B中的binder代理IContentProvider。

技术分享

acquireUnstableProvider的实现在类ApplicationContentResolver里。我们调用Context的getContentResovler返回的就是ApplicationContentResolver的实例,它继承了ContentResolver,代码实现在ContextImpl.java里。

技术分享

这里的mMainThread是ActivityThread,它最终会通过ActivityManagerNative调用到AMS里的getContentProvider。getContentProvider会调getContentProviderImpl。

技术分享

因为是第一次使用ContentProvider,所以通过mProviderMap.getProviderByName得到的cpr为null,最下面providerRunning为false,说明没provider尚未运行。

cpr是ContentProviderRecord类型,像ActivityRecord、ServiceRecord、BroadcastRecord一样,ContentProviderRecord是ContentProvider在AMS里的记录,里面包含了客户端到Provider的连接等信息。

技术分享

因为providerRunning为false,接下来转到PackageManagerService中,调用resolveContentProvider获取cpi,cpi是ProviderInfo类型。在resolveContentProvider中,根据应用安装时获得的provider信息,由PackageParser得到ProviderInfo。然后把这个cpi作为ContentProviderRecord构造函数的参数, new出一个ContentProviderRecord的实例。

技术分享

因为Provider所在进程还没启动,所以proc和proc.thread都为null,走else的分支,通过startProcessLocked来启动provider所在的进程。

技术分享

startProcessLocked需要用到ProviderInfo里的processName,ContentProviderRecord里的ApplicationInfo等信息。

 

 

二、ContentProvider自身启动的时机

应用在启动时,它里面的ContentProvider也会立即被启动,即便这时还没有任何代码会用到ContentProvider,ContentProvider的构造函数和onCreate也会被调用。

在应用启动的入口ActivityThread的main函数中会调attach把客户端的Application交由AMS管理,最终会调到AMS内部的attachApplicationLocked。

技术分享

在AMS中又会回调客户端ActivityThread这一侧的bindApplication,完成应用最后的绑定工作。

技术分享

bindApplication会调到ActivityThread中handleBindApplication,这个函数里面会初始化客户端Application需要用到的数据结构,其中就有provider。

技术分享

installContentProviders就是初始化应用中的ContentProvider组件,第二个参数providers是应用内所有ContentProvider的ProviderInfo结构的列表。data是AppBindData类型,是handleBindApplication的参数,从AMS一侧传过来的。还可以看到ActivityThread中另一个重要的类Instrumentation也是在handleBindApplication里初始化的,并且还是在ContentProvider的后面。AppBindData的结构如下:

技术分享

installContentProvider会对列表中的provider挨个调installProvider,installProvider中会通过反射构造出ContentProvider的实例,并调用它的onCreate方法。

技术分享

ContentProvider的attachInfo中会有一些初始化的动作,最后调onCreate,它的实现如下:

技术分享

可见,只要应用一启动,无论是否马上用到ContentProvider,它都会立即被构建出来,并调用onCreate,一般在onCreate中会做创建表、导入初始数据的动作,这样会占用应用启动的时间,而ContentProvider初始化完成后可能并不会立即被用到,要是能控制并推迟ContentProvider的启动时机就好了。

 

 

三、ContentProvider不自启动

在AndroidManifest.xml中使用android:process和android:multiprocess这两个属性来描述provider可以让ContentProvider不随Application的初始化而启动。我们知道默认不指定android:process的话,组件所在的进程名就是包名,multiprocess默认为false。如果对provider指定android:process=”fore”,同时android:multiprocess=”true”,那么在应用启动时provider就不会随之启动,并且provider并没有在另一个新进程中,仍然在原来应用默认的进程中。只有在后面用到provider时,比如query、insert等调用才会启动provider。

我们仔细看一下之前所述的ContentProvider随应用启动的流程。

 

1. AMS里的attachApplicationLocked

应用启动时,ActivityThread的attach函数最终会调到AMS里的attachApplicationLocked,在它往回调用ActivityThread的bindApplication之前,先会准备好应用中包含的所有provider的list,这是通过函数generateApplicationProvidersLocked实现的。

 

技术分享

generateApplicationProvidersLocked又是通过PMS的queryContentProviders得到apk中的provider列表的。

技术分享

2. PMS里的queryContentProviders

技术分享

queryContentProviders会用到ProviderIntentResolver类里保存的provider列表,mProviders是ProviderIntentResolver类的实例,mProviders.mProviders是ProviderIntentResolver类里的一个HashMap,存放着ComponentName和PackageParser.Provider的对应关系。

技术分享

这个HashMap中的provider信息是在PMS的scanPackage时放入的。在android5.0上有scanPackageDirtyLI这个函数,它会调ProviderIntentResolver类的addProvider方法把PackageParser.Package中的provider信息加到mProviders这个HashMap里。

技术分享

这里的pkg就是PackageParser.Package类型,它里面的providers是在安装apk时解析AndroidManifest.xml文件所得到的。在parseBaseApplication函数中会解析manifest文件,把标签中含有provider的信息加入到PackageParser.Package的mProviders中。

技术分享

在知道queryContentProviders要遍历的providers是从哪儿来的之后,接着再来看queryContentProviders下面的一个判断条件。processName是从AMS里传过来的,ProcessRecord里的进程名,也就是应用所在的进程的进程名,p.info.processNames是从PackageParser.Package中传来的,解析manifest得到的provider组件指定的进程名,在这里android:processName=”fore”,与应用的进程名不一致,所以没被加到list中。

从第二部分的分析可知,后面在ActivityThread中会依次遍历由AMS传来的provider列表,调用installProvider,通过反射newInstance出每个ContentProvider,并调用它们的onCreate。这里因为指定了不同的android:processName,所以不会被加到列表传给ActivityThread,也就不会被启动。

技术分享

 

3. ContentProviderRecord里的canRunHere

那么指定了android:multiprocess=”true”后,为什么ContentProvider没有在单独的另一个进程中运行呢?前面说了ContentProvider如果不在应用初始化时一起启动,那就在被使用时才启动。调用query、insert等ContentResolver的方法时,客户端会调acquireUnstableProvider之类的函数,在AMS端对应调用getContentProviderImpl。

第一部分有分析这个过程,如果ContentProvider实现在应用A中,那么在B中使用provider时,在getContentProviderImpl里会调startProcessLocked来启动应用A所在的进程,但在startProcessLocked之前会先判断是否在同一进程中启动provider就可以了,不用再把provider放在另一个进程中。本来ContentProvider可以分别给位于不同进程的不同客户端提供数据,但如果不是必要的话,ContentProvider在同一进程内部被调用会更节省资源,所以在getContentProviderImpl时有做判断,这个判断就是ContentProviderRecord里的canRunHere函数。下面的代码是在getContentProviderImpl中判断如果canRunHere返回true,就直接返回ContentProviderHolder,不继续往下走去startProcessLocked起新进程了。

技术分享

   canRunHere是在应用的uid和provider这个组件的uid相同的情况下(有可能是uid不同的另一个应用去访问ContentProvider),要么应用的进程名和provider的进程名相同,要么provider声明了multiprocess为true,这样provider就和使用它的客户端在同一进程里了。

技术分享

这样如果应用内有很多个provider,我们就可以不必应用启动时在主线程里初始化这些provider并调用它们的onCreate,而是根据它们实际的使用情况,在调用时才启动相应的provider。

ContentProvider使用拾遗

标签:

原文地址:http://blog.csdn.net/yueqian_scut/article/details/51338789

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!