第八篇 android 内容提供组件机制 .
如下是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发起数据同步调用。
欢迎转载,转载时请尊重原创注明出处。