Content Provider其实很简单,其实质就是IPC通信,通过提供一个IInterface给Client来访问当前进程的数据。下面来分析一下Content Provider的管理过程。
1. ContentResolver
ApplicationContentResolver定义在ContextImpl.java中。
1.1 acquire provider
ApplicationContentResolver是在Application请求对指定Uri进程数据操作时,根据Uri中包含的authority来获取相应的Provider。下面的序列图给出了acquire provider的过程。由上面的时序图中可以看出,Client需要向AMS请求获取相应的provider,这个Provider在AMS中以一个ContentProviderRecord类型的对象来管理的。ContentProviderRecord 是一个Parcelable类型,可以作为AMS返回值返回给Client进程。通过返回ContentProviderHolder对象,AMS可以提供给Client所请求的Provider。
Provider是提供保护数据的接入访问的,我们可以在AndroidManifest.xml中设定它的运行进程,一般情况下,不同进程间,不同Application间的访问只能通过IPC来进行,但那是有些情况是可以允许Client在自己的进程中创建本地Provider来进行访问的。下面介绍一下那些情况下允许本地创建Provider的几种情况:
1. 当Client和Provider处在同一个进程中时,并且Client的UID和Provider的UID相同(UID是系统为每个AndroidManifest.xml中的Application分配的,或者根据设定的android:sharedUserId)时;
2. 当Client和Provider处在同一个进程中,并且Client的UID和Provider的UID不相同,但是Client的UID为System UID;
3. 当Client和Provider处在不同进程中,Provider设置了
android:multiprocess
属性, 并且Client的UID和Provider的UID相同时;4. 当Client和Provider处在不同进程中,Provider设置了
android:multiprocess
属性, 并且Client的UID和Provider的UID不相同时,但是Client的UID是System UID;这里需要说明一下, android的官方文档上说明Provider设置了
android:multiprocess
属性,那么则会在Client访问时在本地创建一个Provider,但是事实上并不是这样的,除了android:multiprocess
属性的设置,还需要UID的匹配。相关的代码在ContentProviderRecord.java
- public boolean canRunHere(ProcessRecord app) {
- return (info.multiprocess || info.processName.equals(app.processName))
- && (uid == Process.SYSTEM_UID || uid == app.info.uid);
- }
1.2 ContentProviderClient
1.3 ContentProviderOperation
- public ContentProviderResult[] applyBatch(String authority,
- ArrayList<ContentProviderOperation> operations)
- throws RemoteException, OperationApplicationException {
- ContentProviderClient provider = acquireContentProviderClient(authority);
- if (provider == null) {
- throw new IllegalArgumentException("Unknown authority " + authority);
- }
- try {
- return provider.applyBatch(operations);
- } finally {
- provider.release();
- }
- }
1.4 Account sync
我们知道Android设备在启动时会要求用户创建并登录一个Google account,并且提供了向该account同步设备信息的功能,如联系人,日程,email等内容。对于应用程序来说,ContentResolver提供了一套Sync同步的接口,在这篇文章中就不再详细的分析数据的Sync过程了,我打算在以后的文章中再分析Sync。如果在做应用程序开发时,的确需要向Account上同步数据,最好的方法是将数据设计为ContentProvider,利用ContentResolver的Sync接口去进行同步工作。
2. ContentProvider
2.1 Permission
2.1.1 Read/Write permission
既然android通过ContentProvdier来实现进程间的数据访问,那么它就需要对保护的数据在被访问时进行权限检测,不但要检测读权限,同时要检测写权限。在AndroidManifest.xml中设置Provider的属性时,有3种permission可以设置,分别为android:permission, android:readPermission, android:writePermission.后2个Permission即为读写权限。而android:permission则代表着读和写2种权限, 但是android:permission只有在readPermission和writePermission未被设置时才有效。
2.1.2 grantUriPermissions
android文档有关于grantUriPermissions的详细说明,这里就简单的提两句,阐述一下grantUriPermissions的使用情况。比如当application A需要使用Component B去访问Provider C,但是Component B并未添加Provider C的Read/Write permission,如果这个Provider设置了属性android:grantUriPermissions,那么就有办法使Component B访问C。通过设置A 启动 B时的Inent属性FLAG_GRANT_READ_URI_PERMISSION and FLAG_GRANT_WRITE_URI_PERMISSION,即可授权B在启动后去访问C。
对于授权permission的具体实现在方法grantUriPermissionFromIntentLocked()中。
有两种通过Inent属性来授权Permission,一种是Client启动新的Activity去访问Provider;
startActivityUncheckedLocked()@ActivityManagerService.java
- mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
- intent, r.getUriPermissionsLocked());
另一种,client启动了一个activity去获取了Uri,这个activity在返回并通过send Result的方式向Client传递Uri的同时(返回的resultCode即是Intent),授权client对Provider的操作权限。下面是两种不同情况下Send result中授权的代码
sendActivityResultLocked()ActivityManagerService.java
- if (callingUid > 0) {
- mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
- data, r.getUriPermissionsLocked());
- }
- if (r.info.applicationInfo.uid > 0) {
- mService.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
- resultTo.packageName, resultData,
- resultTo.getUriPermissionsLocked());
- }
AndroidManifest.xml文件中的element grant-uri-permission和设置android:grantUriPermissions属性起到相同的作用,均需要Client的启动Intent设置FLAG_GRANT_READ_URI_PERMISSION and FLAG_GRANT_WRITE_URI_PERMISSION属性才能访问Provider。不同的是,grant-uri-permission只允许Client访问特定的Provider子集,grant-uri-permission内的参数设定了这个被允许访问的Provider子集。两者冲突时优先判决android:grantUriPermissions。
- <grant-uri-permission android:path="string"
- android:pathPattern="string"
- android:pathPrefix="string" />
2.1.3 Path Permission
AndroidManifest.xml文件中的element path-permission类似于grant-uri-permission,同样是指定某个Provider子集的权限设定;不同的是使用path-permission不需要设置Client Intent属性,并且path-permission单独设置了Read/Write Permission,这样可以使Provider更灵活的给不同的Provider子集设置不同的权限。
- <path-permission android:path="string"
- android:pathPrefix="string"
- android:pathPattern="string"
- android:permission="string"
- android:readPermission="string"
- android:writePermission="string" />
我们知道ContentProvider就是一个提供了数据接入的实现,开发人员在设计一个ContentProvider时,需要确定采用什么样的方式来存储数据,对于不同的数据操作方式,ContentProvide需要提供不同的接入操作,因此在这篇文章中不再介绍具体的ContentProvider操作。ContentProvider最常用的数据存储方式是通过sqlite数据库,在以后有机会分析android的数据库实现机制时在进行详细的说明与分析。