`
810364804
  • 浏览: 784175 次
文章分类
社区版块
存档分类
最新评论

Android ContentProvider详解

 
阅读更多

一.Android四大组件

Android四大组件是Activity, Service, Content Provider, Broadcast Receiver

Activity作为程序界面,直接与用户交互

Service运行在后台,没有界面,完成特定的功能

ContentProvider维护应用数据,方便应用本身或其它应用访问

Broadcast Receiver提供异步广播消息接收机制,便于各应用/组件进行交互

<wbr></wbr>

通过AndroidManifest.xml,<wbr><span style="word-wrap:normal; word-break:normal; font-family:宋体">可以看到一个应用使用了哪些组件:</span></wbr>

<wbr><a target="_blank" href="http://photo.blog.sina.com.cn/showpic.html#blogid=49f62c350101hhhl&amp;url=http://album.sina.com.cn/pic/001lYyXjty6DIKA9Sqzff" style="text-decoration:none; color:rgb(62,159,187)"><img name="image_operate_65281382759067637" src="http://s16.sinaimg.cn/mw690/001lYyXjty6DIKA9Sqzff&amp;690" alt="Android&lt;wbr&gt;ContentProvider详解" title="Android&lt;wbr&gt;ContentProvider详解" style="margin:0px; padding:0px; border:0px; list-style:none"></a><br><wbr><wbr><wbr>attribute<span style="word-wrap:normal; word-break:normal; font-family:宋体">的定义可以参考</span><a target="_blank" href="http://developer.android.com/guide/topics/manifest/manifest-intro.html" style="text-decoration:none; color:rgb(62,159,187)"><span style="word-wrap:normal; word-break:normal; color:windowtext">http://developer.android.com/guide/topics/manifest/manifest-intro.html</span></a></wbr></wbr></wbr></wbr>

<wbr><wbr><span style="word-wrap:normal; word-break:normal; font-family:宋体">下面重点探讨</span>Content Provider<span style="word-wrap:normal; word-break:normal; font-family:宋体">的实现和使用。</span></wbr></wbr>

<wbr></wbr>

二.什么是ContentProvider

Content Provider维护特定的应用数据,并可以让其它应用轻松访问该数据。对数据使

用者来说它是数据提供者。它提供统一的接口对数据进行操作,使用者不用关心数据到底是如何存储的以及数据类型到底是什么。也就是说,Content Provider作为数据提供者,提供了对外共享本地数据一种机制,使Android应用能方便地基于该机制进行数据访问。

<wbr><wbr><wbr><span style="word-wrap:normal; word-break:normal; font-family:宋体">为了便于管理和访问,每个</span>Content Provider<span style="word-wrap:normal; word-break:normal; font-family:宋体">必须有唯一标示,用</span>Uri<span style="word-wrap:normal; word-break:normal; font-family:宋体">表示。</span>Uri<span style="word-wrap:normal; word-break:normal; font-family:宋体">类似</span>http url,<span style="word-wrap:normal; word-break:normal; font-family:宋体">构成如下:</span></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><strong>content://authority/path</strong></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><span style="word-wrap:normal; word-break:normal; font-family:宋体">所有</span>Content Provider<span style="word-wrap:normal; word-break:normal; font-family:宋体">的</span>Uri<span style="word-wrap:normal; word-break:normal; font-family:宋体">必须以</span>content://<span style="word-wrap:normal; word-break:normal; font-family:宋体">开头,这是</span>Android<span style="word-wrap:normal; word-break:normal; font-family:宋体">规定的。</span></wbr></wbr></wbr>

<wbr><wbr><wbr>authority<span style="word-wrap:normal; word-break:normal; font-family:宋体">是个字符串,它由开发者自己定义,用于来唯一标示一个</span>ContentProvider<span style="word-wrap:normal; word-break:normal; font-family:宋体">。系统会根据这个标示查找</span>ContentProvider<span style="word-wrap:normal; word-break:normal; font-family:宋体">。</span></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr>path<span style="word-wrap:normal; word-break:normal; font-family:宋体">也是字符串,表示要操作的数据。可根据自己的实现逻辑来指定:</span><span style="word-wrap:normal; word-break:normal; line-height:18px; font-family:Verdana,sans-serif; color:black; font-size:9pt"><br></span>content://contacts/people<span style="word-wrap:normal; word-break:normal; font-family:宋体">表示要操作</span>ContentProvider<span style="word-wrap:normal; word-break:normal; font-family:宋体">为</span>contacts<span style="word-wrap:normal; word-break:normal; font-family:宋体">下的</span>people<span style="word-wrap:normal; word-break:normal; font-family:宋体">表</span></wbr></wbr></wbr></wbr>

content://com.android.contacts/people/#表示要操作表people中特定id的行(记录)

content://downloads/download/10/name表示要操作id10的行的name字段。

content://downloads/download/*表示操作download表中的所有字段。

总之,#匹配一个数字字符串,*匹配一个文本字符串。

<wbr></wbr>

三.ContentProvider的实现和使用<wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr>

Android<wbr>ContentProvider详解

<wbr><wbr><wbr><wbr><span style="word-wrap:normal; word-break:normal; font-family:宋体">可以看出</span>,<span style="word-wrap:normal; word-break:normal; font-family:宋体">实现一个自定义的</span>Content Provider<span style="word-wrap:normal; word-break:normal; font-family:宋体">,要基于系统提供的基类</span>ContentProvider<span style="word-wrap:normal; word-break:normal; font-family:宋体">,需要实现</span>6<span style="word-wrap:normal; word-break:normal; font-family:宋体">个接口。大部分接口就是类似数据库的数据操作接口,实际上</span>Content Provider<span style="word-wrap:normal; word-break:normal; font-family:宋体">是需要创建数据库并对数据库进行操作的。完成实现之后,在</span>Androidmanifest.xml<span style="word-wrap:normal; word-break:normal; font-family:宋体">中声明自己的</span>Content Provider<span style="word-wrap:normal; word-break:normal; font-family:宋体">以及与</span>Provider<span style="word-wrap:normal; word-break:normal; font-family:宋体">相关的</span>permission<span style="word-wrap:normal; word-break:normal; font-family:宋体">声明</span>(<span style="word-wrap:normal; word-break:normal; font-family:宋体">可以没有</span>permission<span style="word-wrap:normal; word-break:normal; font-family:宋体">定义</span>)<span style="word-wrap:normal; word-break:normal; font-family:宋体">。例如:</span></wbr></wbr></wbr></wbr>

Android<wbr>ContentProvider详解
<wbr><wbr><wbr><wbr>最后整个应用被编译成</wbr></wbr></wbr></wbr>apk。安装之后,该应用里的contentProvider就可以被其它应用访问了。对于Provider使用者来说, <wbr><span style="word-wrap:normal; word-break:normal; font-family:宋体">如果特定</span>Provider<span style="word-wrap:normal; word-break:normal; font-family:宋体">有</span>permission<span style="word-wrap:normal; word-break:normal; font-family:宋体">要求,则要在自己的</span>Androidmanifest.xml<span style="word-wrap:normal; word-break:normal; font-family:宋体">中添加指定</span>Permission<span style="word-wrap:normal; word-break:normal; font-family:宋体">引用</span>,<span style="word-wrap:normal; word-break:normal; font-family:宋体">如:</span></wbr>

Android<wbr>ContentProvider详解

<wbr><wbr><wbr><wbr><span style="word-wrap:normal; word-break:normal; font-family:宋体">使用非常简单,</span>Android<span style="word-wrap:normal; word-break:normal; font-family:宋体">提供了</span>Context<span style="word-wrap:normal; word-break:normal; font-family:宋体">级别的</span>ContentResolver<span style="word-wrap:normal; word-break:normal; font-family:宋体">对象来对</span>Content Provider<span style="word-wrap:normal; word-break:normal; font-family:宋体">进行操作。正是因为有了</span>ContentResolver<span style="word-wrap:normal; word-break:normal; font-family:宋体">,</span><span style="word-wrap:normal; word-break:normal; font-family:宋体">使用者才不用关心</span>Provider<span style="word-wrap:normal; word-break:normal; font-family:宋体">到底是哪个应用或哪个类实现的。只要知道它的</span>uri<span style="word-wrap:normal; word-break:normal; font-family:宋体">就能访问。</span>ContentResolver<span style="word-wrap:normal; word-break:normal; font-family:宋体">对象存在于每个</span>Context<span style="word-wrap:normal; word-break:normal; font-family:宋体">中。几乎所有对象都有自己的</span>Context<span style="word-wrap:normal; word-break:normal; font-family:宋体">。</span></wbr></wbr></wbr></wbr>

Android<wbr>ContentProvider详解
<wbr><wbr><wbr><wbr>有些情况下,</wbr></wbr></wbr></wbr>Content Provider使用者想监听数据的变化,可以注册一个Observer:

<wbr></wbr>

Android<wbr>ContentProvider详解


<wbr></wbr>

四.ContentProvider内部机制

<wbr></wbr>

1ContentProvider接口调用过程

<wbr><wbr><wbr><wbr>ContentProvider<span style="word-wrap:normal; word-break:normal; font-family:宋体">依赖</span>ContentResolver/ActivityThread/ActivityManagerService<span style="word-wrap:normal; word-break:normal; font-family:宋体">对外提供</span></wbr></wbr></wbr></wbr>

服务。虽然ContentProvider的用法以及表现形式不是一个Service,实际上它可以看作是ActivityManagerService提供的一种服务,它实现了IBinder接口。

<wbr><wbr><wbr><wbr><span style="word-wrap:normal; word-break:normal; font-family:宋体">首先调用者通过特定</span>uri<span style="word-wrap:normal; word-break:normal; font-family:宋体">调用特定</span>ContentProvider<span style="word-wrap:normal; word-break:normal; font-family:宋体">的接口函数,比如</span>insert(),<wbr><span style="word-wrap:normal; word-break:normal; font-family:宋体">此时</span>ContentResolver<span style="word-wrap:normal; word-break:normal; font-family:宋体">会通过</span>uri<span style="word-wrap:normal; word-break:normal; font-family:宋体">获取特定</span>ContentProvider<span style="word-wrap:normal; word-break:normal; font-family:宋体">的实例,</span>ActivityThread<span style="word-wrap:normal; word-break:normal; font-family:宋体">检查本地</span>Cache<span style="word-wrap:normal; word-break:normal; font-family:宋体">,如果发现此</span>ContentProvider<span style="word-wrap:normal; word-break:normal; font-family:宋体">已经被引用过,则直接直接取出</span>ContentProvider<span style="word-wrap:normal; word-break:normal; font-family:宋体">返回给调用者。如果没有发现,由于</span>ContentProvider<span style="word-wrap:normal; word-break:normal; font-family:宋体">可能已经被</span>load<span style="word-wrap:normal; word-break:normal; font-family:宋体">了,可能还没有</span>load<span style="word-wrap:normal; word-break:normal; font-family:宋体">;可能要创建</span>Process<span style="word-wrap:normal; word-break:normal; font-family:宋体">,可能要检查</span>permission<span style="word-wrap:normal; word-break:normal; font-family:宋体">,所以</span>ActivityThread<span style="word-wrap:normal; word-break:normal; font-family:宋体">调用到</span>ActivityManagerService<span style="word-wrap:normal; word-break:normal; font-family:宋体">来进行相关处理</span>/<span style="word-wrap:normal; word-break:normal; font-family:宋体">检查。如果该</span>Provider<span style="word-wrap:normal; word-break:normal; font-family:宋体">是</span>Single Process<span style="word-wrap:normal; word-break:normal; font-family:宋体">,</span>ActivityManagerService<span style="word-wrap:normal; word-break:normal; font-family:宋体">会为</span>ContentProvider<span style="word-wrap:normal; word-break:normal; font-family:宋体">创建一个独立</span>Process<span style="word-wrap:normal; word-break:normal; font-family:宋体">;如果是</span>MultiProcess<span style="word-wrap:normal; word-break:normal; font-family:宋体">,说明每个调用者可以拥有独立的</span>ContentProvider<span style="word-wrap:normal; word-break:normal; font-family:宋体">实例,于是</span>ActivityManagerService<span style="word-wrap:normal; word-break:normal; font-family:宋体">只是返回</span>ContentProvider<span style="word-wrap:normal; word-break:normal; font-family:宋体">的相关信息给</span>ActivityThread<span style="word-wrap:normal; word-break:normal; font-family:宋体">,由</span>ActivityThread<span style="word-wrap:normal; word-break:normal; font-family:宋体">负责</span>ContentProvider<span style="word-wrap:normal; word-break:normal; font-family:宋体">的实例化,此时</span>ContentProvider<span style="word-wrap:normal; word-break:normal; font-family:宋体">运行在调用者</span>Process<span style="word-wrap:normal; word-break:normal; font-family:宋体">中。实例化后,</span>IConentProvider<span style="word-wrap:normal; word-break:normal; font-family:宋体">会返回给调用者,通过该接口可以调用所需功能。</span></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr>ActivityThread<span style="word-wrap:normal; word-break:normal; font-family:宋体">本地维护一个</span>mProviderMap &lt;ProviderName, ProviderRecord &gt;<span style="word-wrap:normal; word-break:normal; font-family:宋体">,记录已被引用的</span>ContentProvider, <wbr><span style="word-wrap:normal; word-break:normal; font-family:宋体">同时使用引用计数</span>mProviderRefCountMap &lt;IBinder, ProviderRefCount&gt;<span style="word-wrap:normal; word-break:normal; font-family:宋体">记录特定</span>ContentProvider<span style="word-wrap:normal; word-break:normal; font-family:宋体">的引用情况。</span></wbr></wbr></wbr></wbr></wbr>

Android<wbr>ContentProvider详解

<wbr></wbr>

2ContentProvider实例创建过程

<wbr><wbr><wbr><wbr>ContentProvider实例的创建与multiprocess属性有关系(Androidmanifest.xml里指定),个人认为理解成多进程并不准确。应该理解为ContentProvider的多实例,不会存在多个ContentProvider进程的情况,ContentProvider 可能存在多个实例。</wbr></wbr></wbr></wbr>

1)对于android:multiprocess=trueContentProvider,意味着可以多实例,那么由调用者在自己的进程空间实例化一个ContentProvider对象,此时定义ContentProvider的App可能并没有启动

Android<wbr>ContentProvider详解

<wbr></wbr>

注意:ContentProvider是否多实例,还得看contentProvideruid与调用者的uid是否相同或contentProvideruidSystem user。具体逻辑是:

public boolean canRunHere(ProcessRecord app) {

<wbr><wbr><wbr><wbr><wbr><wbr><wbr>return (info.multiprocess || info.processName.equals(app.processName))</wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>&amp;&amp; (uid == Process.SYSTEM_UID || uid == app.info.uid);</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

}

<wbr></wbr>

2)对于android:multiprocess=false(默认值)的ContentProvider,由系统把定义该ContentProvider的App启动起来(一个独立的Process)并实例化ContentProvider,这种ContentProvider只有一个实例,运行在自己App的Process中。所有调用者共享该ContentProvider实例,调用者与ContentProvider实例位于两个不同的Process

Android<wbr>ContentProvider详解

<wbr></wbr>

其中Process.start()->zygoteSendArgsAndGetPid()->ZygoteInit.runSelectLoopMode()

-> ZygoteConnection.runOnce() -> Zygote.forkAndSpecialize()->RuntimeInit.zygoteInit()

-> invokeStaticMain()->MethodAndArgsCaller.run()->Method.invokeNative

->ActivityThread.main(),这是通用的APK启动流程。

<wbr></wbr>

3ContentProvider的加载/发布过程

<wbr><wbr><wbr></wbr></wbr></wbr>ContentProvider不能单独发布,总是被打包到某个Android应用(apk)里。APK被安装之后,实例化之后每个Android应用都有一个Application实例,每个ActivityService有一个ApplicationContext实例。

<wbr><wbr><wbr>Android<span style="word-wrap:normal; word-break:normal; font-family:宋体">应用程序的入口函数是</span>ActivityThread.main(),<span style="word-wrap:normal; word-break:normal; font-family:宋体">该函数不仅创建了</span>ActivityThread<span style="word-wrap:normal; word-break:normal; font-family:宋体">实例以及消息循环机构,而且创建了</span>ApplicationThread<span style="word-wrap:normal; word-break:normal; font-family:宋体">实例,通过此实例向Activity Manager</span><wbr>Service(AMS)提供IApplicationThread接口,AMS正是通过该接口调度和管理Activity。<wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr>ActivityThread<span style="word-wrap:normal; word-break:normal; font-family:宋体">通过</span>attachApplication()<span style="word-wrap:normal; word-break:normal; font-family:宋体">把自己的</span>ApplicationThread<span style="word-wrap:normal; word-break:normal; font-family:宋体">实例告知</span>AMS<span style="word-wrap:normal; word-break:normal; font-family:宋体">。</span></wbr></wbr></wbr>

AMS根据thread信息更新进程记录(ProcessRecord)并调用threadbindApplication()进行初始化工作并创建ApplicationContextApplication实例,然后安装package里声明的所有contentProvider主要过程如下:

Android<wbr>ContentProvider详解

AMS维护了很多信息,其中比较重要的有:

mProcessNames<wbr><wbr><wbr><wbr><wbr><span style="word-wrap:normal; word-break:normal; font-family:宋体">包名</span>(processName)<span style="word-wrap:normal; word-break:normal; font-family:宋体">和进程信息</span>(ProcessRecord)<span style="word-wrap:normal; word-break:normal; font-family:宋体">映射表</span></wbr></wbr></wbr></wbr></wbr>

mProvidersByName:<wbr><wbr></wbr></wbr>Provider发布名和Provider信息(ContentProviderRecord)映射表

mProvidersByClass<wbr>Provider<span style="word-wrap:normal; word-break:normal; font-family:宋体">类名和</span>Provider<span style="word-wrap:normal; word-break:normal; font-family:宋体">信息</span>(ContentProviderRecord)<span style="word-wrap:normal; word-break:normal; font-family:宋体">映射表</span></wbr>

conProviders属于ProcessRecord信息,特定Process正在使用的ContentProvider及其个数映射表<wbr><wbr><wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

pubProviders属于ProcessRecord信息,特定Process已经PublishedProvider名和Provider信息

<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>(ContentProviderRecord)<span style="word-wrap:normal; word-break:normal; font-family:宋体">映射表</span></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

ActivityThread维护了3个与ContentProvider相关的Map

mProviderMap<wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><span style="word-wrap:normal; word-break:normal; font-family:宋体">记录本应用已使用的</span>Provider<span style="word-wrap:normal; word-break:normal; font-family:宋体">信息:</span>&lt;Provider<span style="word-wrap:normal; word-break:normal; font-family:宋体">发布名</span>, ProviderRecord&gt;</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr>

mProviderRefCountMap:<wbr><span style="word-wrap:normal; word-break:normal; font-family:宋体">记录本应用已使用的</span>Provider<span style="word-wrap:normal; word-break:normal; font-family:宋体">引用计数信息:&lt;IBinder, ProviderRefCount&gt;</span></wbr>

mLocalProviders<wbr><wbr><wbr><wbr><wbr></wbr></wbr></wbr></wbr></wbr>记录本地加载的Provider信息:<IBinder, ProviderRecord>

<wbr></wbr>

4ContentProvider通知机制

Android<wbr>ContentProvider详解
<wbr></wbr>

注意:这个通知机制需要ContentProvider的实现者在实现insert/delete/query/update接口时调用ContentResolvernotifyChange(),否则没法实现数据变化的通知。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics