第八篇 android 内容提供组件机制 .

标签: android | 发表时间:2012-08-10 19:24 | 作者:固弘博客
出处:http://www.cnblogs.com/
       内容提供组件提供了一个进程安全访问另一个进程数据的方法和接口,该接口对数据进行了封装,使客户端可以以一致的接口跨进程访问各种数据。

       如下是ContentProvider的系统类图:

      

 

 

      

       图中左边部分是客户端部分,右边部分是处于另外进程的ContentProvider组件和内容提供的服务部分。整个系统采用的模式与MVC模式类似。

       客户端主要的类是ContentResolver和ContentObserver,客户端通过ContentResolver类提供的接口与ContentProvider组件和内容提供服务进行跨进程访问,如数据的查询,数据的CRUD(create, retrieve, update, and delete)操作,数据监控对象的登记、数据的同步等操作,ContentResolver相当于MVC模式的控制器部分。

            ContentResolver是一个抽象接口,ContentResolver的具体类由ContextImpl对象进行实例化。客户端通过ContentResolver根据标识ContentProvider的 content URL(一个 content URL包括对应Provider的authoriry名称和对应表和行的部分的 path名称)找到要访问的ContentProvider对应的BINDER引用,然后通过ContentProviderProxy采用远程代理方式访问Provider。

             ContentObserver是作为数据观察用的一个抽象类,ContentObserver内部封装了一个Transport 桩类,提供了跨进程访问的能力,一个ContentObserver对象可以通过ContentResolver登记到ContentService中,一个ContentObserver对象根据标识Provider数据的 content URI以树的形式登记到ContentService中;也可以以ContentObserver数组形式登记到光标对象中,在光标变化时得到通知。ContentObserver对象相当于MVC模式的视图,在数据变化时每一个登记的ContentObserver对象都会得到通知。

         右边部分的ContentService和ContentProvider类相当于MVC模式的模式部分。

              ContentService是一个由框架的SystemServer服务启动的系统服务类,实现ContentObserver对象的登记和更新通知及数据同步等功能。

              ContentProvider类是实现内容提供组件的最重要的一个类,本身是一个抽象类,提供其它应用进行跨进程访问数据的一个接口,其它应用通过该接口访问内容提供组件封装的数据。

         内容提供组件开发者必须实现ContentProvider类的具体类,用来封装要对其它应用公开的数据,通过内容提供组件提供和封装的数据可以是数据库、数组之类的结构化数据,也可以文件,甚至是云端数据。内容提供组件使用统一资源标示符(URI )标示每一个ContentProvider及         其提供的数据。ContentProvider类内部也提供一个Transport 桩类,提供了能够被其它应用跨进程访问的能力。

         每一个ContentProvider组件在第一次获取时被加载到内存,同时以ContentProviderRecord的形式被保存到活动管理服务中的两个HashMap中,一个HashMap以Content Provider的authoriry名称作为键值保存,另外一个以Content Provider的类名作为键值来保存。同时也以ProviderClientRecord的形式保存到获取ContentProvider组件的应用程序进程(ActivityRecord)中的两个HashMap中,一个也以Content Provider的authoriry名称作为键值保存,另一个以Content Provider对应的Binder对象作为键值来保存,在下次获取相同Provider时可以通过getExistingProvider函数从这两个HashMap中直接获得。

         内容提供组件通过Cursor接口返回查询后的数据。Cursor对应表格形式的数据,Cursor类相当于一个包含多个数据行的列表类,提供了以行和列随机存取ContentProvider封装数据的方式,通过Cursor类可以遍历每一行的数据,还可以访问每一行的每一列的内容。

         如下是Content Provider组件使用到的光标类图。

         

                整个类图采用了装饰模式、适配器模式、观察者模式等设计模式。

             客户端通过ContentResolver的query函数返回的光标对象为CursorWrapperInner类型,CursorWrapperInner是ContentResolver通过ContentProviderProxy代理调用query函数返回的BulkCursorToCursorAdaptor类型光标对象的包装,采用了装饰模式。

          具体ContentProvider的query函数返回的光标类型是与具体封装的数据对应的,对于数据库查询使用的是SQLiteCursor类型,如CalendarProvider、ContactsProvider等;数组类型使用的光标类型是MatrixCursor,如gallery3d应用的GalleryProvider采用就是MatrixCursor类型的光标。光标类型本身不是Binder对象,不能跨进程调用,因此为了客户端能够跨进程调用光标函数, ContentProvider内部的本地STUB对象在调用具体ContentProvider的query函数返回时,对返回的实际光标对象采用CursorToBulkCursorAdaptor对象采用了适配器模式进行了包装,CursorToBulkCursorAdaptor通过BulkCursorNative类继承自Binder,因此是一个Binder对象,因此客户端可以跨进程访问该对象。具体ContentProvider的query函数返回的光标对象作为CursorToBulkCursorAdaptor的一个成员,对CursorToBulkCursorAdaptor对象的调用实际都转给具体的光标对象处理。

        在ContentProviderProxy代理端,BulkCursorToCursorAdaptor对象又对ContentProvider的本地STUB对象返回的CursorToBulkCursorAdaptor对象采用适配器模式进行了包装,CursorToBulkCursorAdaptor对象作为BulkCursorToCursorAdaptor对象成员,对BulkCursorToCursorAdaptor对象的函数访问都转给CursorToBulkCursorAdaptor对象的相同函数处理。

       在通过ContentProviderProxy代理进行query函数调用时,也把BulkCursorToCursorAdaptor对象的一个内部SelfContentObserver对象通过BINDER调用传给ContentProvider的本地端,ContentProvider的本地端在CursorToBulkCursorAdaptor封装光标时也把该SelfContentObserver对象登记到具体光标对象中,因此当光标发生变化时SelfContentObserver对象得到通知,再通过SelfContentObserver对象通知在客户端的光标对象中登记的其它ContentObserver对象。这是观察者设计模式的采用。

          下图是客户端对Provider进行一次查询的序列图。

         

    

    1)客户端应用通过getContentResolver函数获得ContentResolver对象,向该对象发起QUERY请求,ContentResolver对象首先调用acquireProvider函数获得一个Provider,函数acquireProvider实际调用ContentResolver具体类ApplicationContentResolver的acquireProvider函数;

     2)ApplicationContentResolver的acquireProvider函数通过其内部ActivityThread对象转而调用ActivityThread对象的acquireProvider函数;

     3)ActivityThread的acquireProvider函数首先调用acquireExistingProvider函数,在本对象的mProviderMap HashMap中根据要获取的Provider的authoriry名称(从Provider的URL中获得)查找要获取的Provider的是否已经存在,如果已经存在,增加Provider的引用计数,返回获得的Provider;

     4)否则调用ActivityManagerService 服务的getContentProvider函数在ActivityManagerService 服务中查找登记的ContentProvider。getContentProvider函数实际调用getContentProviderImpl函数,getContentProviderImpl函数首先在ActivityManagerService 服务的mProvidersByName HashMap中根据要获取的Provider的authoriry名称查找Provider是否已经存在;如果已经存在,判断是否对该Provider有许可权限;如果有许可权线,则判断Provider是否能够在客户端进程运行;若允许则根据找到的ContentProviderRecord对象克隆一个ContentProviderRecord后返回,否则直接返回找到的ContentProviderRecord;如果在mProvidersByName 的HashMap中没有找到则需要根据包管理器的resolveContentProvider函数获得Provider的包信息;获得Provider的包信息后根据包名从mProvidersByClass的HashMap中查找;如果在mProvidersByClass的HashMap中没有找到则重新实例化一个新的ContentProviderRecord;然后判断Provider是否可以在客户端进程运行,是则返回;否则则启动新的进程加载该Provider;加载后把Provide添加到mProvidersByName和mProvidersByClass两个HashMap中,返回ContentProviderRecord;

     5)ActivityThread的acquireProvider函数继续调用installProvider在本地mProviderMap和mLocalProviders两个HashMap中添加ProviderClientRecord记录,一个以authoriry名称为键值,另一个以Provider对应的Binder对象为键值;并增加该Provider的引用计数,把该Provider的引用计数以Provider对应的Binder对象为键值添加到mProviderRefCountMap中;ActivityThread的acquireProvider函数最后返回Provider对应的Binder对象;

     6)获得Provider后ContentResolver对象接着调用ContentProviderProxy代理的query函数;

     7)ContentProviderProxy代理的query函数中首先实例化一个BulkCursorToCursorAdaptor对象。然后对参数进行序列化,在序列化时也把BulkCursorToCursorAdaptor对象中的SelfContentObserver对象作为参数进行序列化,对参数进行序列化后通过ContentProvider本地引用向ContentProvide本地桩发起transact请求;

     8)ContentProvide本地桩在onTransact中处理binder请求,对于QUERY_TRANSACTION请求,先反序列化BINDER参数后,调用具体ContentProvider对象的query函数;

     9)调用具体ContentProvider对象的query函数后,先实例化一个具有远程通讯能力的CursorToBulkCursorAdaptor对象,CursorToBulkCursorAdaptor对象对返回的具体Cursor对象进行包装。在CursorToBulkCursorAdaptor对象中也根据transact请求传进来的SelfContentObserver对象的引用构造一个代理对象登记到具体光标对象中;在CursorToBulkCursorAdaptor实例化后接着通过CursorToBulkCursorAdaptor对象发起一次读取数据记录数的调用,然后把读取的数据记录数及CursorToBulkCursorAdaptor作为binder对象序列化到返回参数中,请求处理完成返回;

     10)ContentProviderProxy代理对象在transact请求返回后读取返回的参数,根据返回的参数初始化BulkCursorToCursorAdaptor对象,把返回的CursorToBulkCursorAdaptor类型的 binder对象引用及读取的数据记录数等参数保存到BulkCursorToCursorAdaptor对象中,返回初始化的BulkCursorToCursorAdaptor对象;

     11)ContentResolver发起query请求返回后根据返回的BulkCursorToCursorAdaptor对象实例化一个CursorWrapperInner对象,对BulkCursorToCursorAdaptor对象进行包装,返回包装后CursorWrapperInner对象给应用,query调用完成。

     下图是客户端登记ContentObserver和发出数据变化通知的流程。

    

 

     1)            客户端应用通过getContentResolver函数获得ContentResolver对象,向该对象发起registerContentObserver调用;

     2)            在registerContentObserver函数中通过getContentService接口获得ContentService对象的远端代理引用,通过ContentService的远端代理引用向ContentService服务发起registerContentObserver远程调用;

     3)            在ContentService服务的registerContentObserver函数中首先调用mRootNode对象的addObserverLocked函数把ContentObserver对象以ObserverEntry记录的形式登记到ObserverNode树(根据Provider的URI 构造)中,登记完成。

     4)            客户端应用通过getContentResolver函数获得ContentResolver对象,向该对象发起notifyChange数据变化通知请求;

     5)            在ContentService服务的notifyChange函数中首先调用mRootNode对象的collectObserversLocked函数在ObserverNode树中查找登记的ContentObserver对象,每个找到的ContentObserver以ObserverCall对象记录的形式保存到ObserverCall对象数组中,返回;

     6)            接着ContentService服务的notifyChange函数在ObserverCall对象数组中遍历找到的ContentObserver对象,调用ContentObserver对象远程代理引用的onChange函数;

     7)            ContentObserver对象远程代理的onChange函数转而调用ContentObserver本地桩对象的onChange函数,然后ContentObserver本地桩对象的onChange函数调用具体的ContentObserver对象的onChange函数,完成数据变化通知;

    8)            在ContentService服务的notifyChange函数中,在完成数据变化通知后如果syncToNetwork参数为真,则通过SyncManager对象发起数据同步请求。在该步首先通过getSyncManager函数获得一个SyncManager对象;

    9)            调用SyncManager对象的scheduleLocalSync发起数据同步调用。

                                   欢迎转载,转载时请尊重原创注明出处。

本文链接

相关 [android] 推荐:

Android 遥控车

- CasparZ - LinuxTOY
您确定您真的会用 Android 手机玩赛车. 16 岁的法国学生 Jonathan Rico 使用 Android 手机通过蓝牙实现了对改装玩具汽车的遥控. 操控的方式和那些标榜的智能手机游戏一样,使用重力感应,差别是这次控制的是现实世界中的遥控汽车. 收藏到 del.icio.us |.

Android免费?毛

- Ruby - FeedzShare
来自: 36氪 - FeedzShare  . 发布时间:2011年08月17日,  已有 2 人推荐. 微软CEO Steve Ballmer在预测竞争对手产品时通常口无遮拦. 比如他去年抨击Google的Android战略时,很多人都不屑一顾. 接着Android蚕食了微软的地盘,后来又开始侵犯苹果的地盘.

GetEd2k (Android应用)

- 某牢 - eMule Fans 电骡爱好者
GetEd2k是一个Android应用程序,作者是anacletus. 此应用可以帮助你把网页中的电驴(eDonkey) 链接添加到你个人电脑的电驴客户端里,不过前提是你的客户端开启了用于远程控制的Web interface(Web服务器,网页接口,Web界面),当然,eMule(电骡), MLDonkey 和 aMule 都支持该功能,所以这三种主流电驴客户端的用户都可以使用GetEd2k.

Android 4.0发布

- coofucoo - Solidot
Shawn the R0ck 写道 "2011年10月19日早上10点,谷歌与三星联手在香港发布了Android 4.0和Galaxy Nexus. " Android 4.0 的主要特性包括:更精细的UI,加强多任务和通知功能,锁屏下可打开摄像头和浏览通知,改进文本输入和拼写检查;增强视频录制和图像编辑功能,支持剪裁和旋转图片、消除红眼、添加效果等;面部识别解锁;Android Beam允许两台支持NFC的设备之间交换应用程序、联系人、音乐和视频;Wi-Fi Direct,蓝牙HDP,等等.

NoScript For Android发布

- John - Solidot
用于屏蔽脚本的浏览器流行扩展NoScript发布了Android版本. 开发者称已经在Firefox for Android测试过,此外也应该能工作在基于Maemo的设备上. 移动版NoScript可以帮助移动用户抵抗基于脚本的攻击. Android平台上的扩展功能和桌面版相似,允许用户对每个网站单独设置脚本执行许可.

Android入门:ContentProvider

- - ITeye博客
一、ContentProvider介绍. ContentProvider翻译为“内容提供者”;. 定义:指该应用包含一些方法,供外界访问,其他应用程序可以调用该方法,比如如果应用A创建了一个数据库“test.db”,默认是私有的,即其他应用程序不能对其进行操作,但是如果应用A使用了ContentProvider,则其他应用程序可以访问该数据库;.

Android Service 详解

- - CSDN博客移动开发推荐文章
一个Service也是一种应用程序组件,它运行在后台以提供某种服务,通常不具有可见的用户界面. 其它的应用程序组件可以启动一个Service,即使在用户切换到另外一个应用程序后,这个Service还是一直会在后台运行. 此外,一个应用程序也可以绑定到一个Service然后使用进程间通信(IPC)方式与Service之间发生交互.

android动画

- - CSDN博客移动开发推荐文章
一、        开发资料与实例教程. 分析android动画模块. Android 动画类的特点和区别. Android动画基础--本文转载自--springfieldx的文章,在此向他致谢. Android Animation 动画效果. Android Tween动画(一). Android Tween动画(二).

Android ContentProvider总结

- - CSDN博客推荐文章
1) ContentProvider为存储和读取数据提供了统一的接口. 2) 使用ContentProvider,应用程序可以实现数据共享. 3) android内置的许多数据都是使用ContentProvider形式,供开发者调用的(如视频,音频,图片,通讯录等). 1)ContentProvider简介.