Content Provider 详解
几个概念:Cursor、 Content provider 、 Uri 、contentresolver
1、
Cursor : 个人理解为数据库中的一行数据,它是每行数据的集合。它是一个类。通过它的一系列方法,我们可以对数据库中的每行进行定位,我们还可以知道每一列的信息。比如:
cursor(游标).moveToFirst(),表示定位到第一行,然后我们通过其他方法可以知道每列的名称,每列的数据类型等。
2、
Content Provider :内容提供者,我们一个application中的其他类假如想操作数据库中的数据的话,就可以直接对此进行操作,而避免了对数据库进行直接操作,再说了,数据库这些个东西有权限要求的,我们不可能把数据库直接暴露出来,所以,通常采用这种形式。其实他就是一个提供数据访问的网站,我们要访问它的话,就得知道他的域名。 android:authorities . 在minifest.xml中配置如下:(包含两个部分,name 与 anthorities)
<provider android:name=".PersonProvider" android:authorities=
"com.sharpandroid.providers.personprovider"/>
当某个应用,可能是外部的,可以通过这两个属性来找到这个Content Provider了。
Content Provider 支持在多个应用中存储和读取数据。这也是跨应用共享数据的唯一方式。在android系统中,没有一个公共的内存区域,供多个应用共享存储数据。
3、
Uri: 这个东西就是 提供者提供数据的详细地址,到底是哪些数据,用此来对数据进行过滤操作。假如说,Content Provider是一个DNS,域名的话。那么Uri可以认为是IP地址,我们通过此IP地址找到我们所需要的数据。知道了它,就知道了数据在哪,然后通过调用某个方法,返回一个Cursor对象。数据包含在这个其中。
它有几个部分组成: content://com.sharpandroid.provider.personprovider/person/2
其中,scheme:content 表示这个数据被一个content类型的数据(内容提供者)所控制。
authrities: com.sharpandroid.provider.personpervider表示权限部分,就是contentprovider的域名部分。。
path:用来请求数据类型的路径。person/2 。请求的是person/2,还可以是school/student等。
ID:被请求特定记录的ID。
还有一点需要注意,需要被数据不一定来自数据库,还可以来自其他地方,比如说XML,文本等。
Uri.parse()这个方法可以将字符串解析为Uri的形式。
我们一般通过UriMather类用来对我们 我们传递过来的IP进行自动匹配。
private static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
private static final int PERSONS = 1;
private static final int PERSON = 2;
private DatabaseHelper databaseHelper;
static{
matcher.addURI("com.sharpandroid.providers.personprovider",
"person", PERSONS); //对特定的值进行匹配,成功返回1.
matcher.addURI("com.sharpandroid.providers.personprovider",
"person/#", PERSON);//#表示通配符,如果匹配成功,返回匹配码2.
}
4、
contentresovler:
那么我们该如何使用contentprovider呢?
我们一般通过contentresolver来对内容提供者提供的数据进行操作。
这个类提供了与Content Provider 相同的四个方法,增删改查方法。
如果需要查询contentprovider数据集的特定记录(行),还需要知道该记录的ID的值。
构建查询
查询就是输入URI等参数,其中URI是必须的,其他是可选的,如果系统能找到URI对应的contentprovider将返回一个Cursor对象。
可以通过ContentResolver.query()或者Activity.managedQuery()方法。两者的方法参数完全一样,查询过程和返回值也是相同的。
其他注意点:
query()方法,返回值是Cursor实例,用于迭代请求的数据。Cursor是一个接口。android为该接口提供了一些只读的(和 JDBC的ResultSet不一样,后者还提供可写入的可选特性)Cursor实现。比如SQLiteCursor,可迭代SQLite数据库中的数据。可以通过SQLiteDatabase类的query()方法获取到该Cursor实例。还有其他的Cursor实现,比如 MatrixCursor,用于数据不是存储在数据库的情况下。
因为Contentprovider可能被多个ContentResolver对象在不同的进程和线程中调用,因此实现Contentprovider必须考虑线程安全问题。
作为良好的习惯,在实现编辑数据的代码中,要调用ContentResolver.notifyChange()方法,通知那些监听数据变化的监听器。
在实现子类的时候,还有一些步骤可以简化Contentprovider客户端的使用:
定义public static final Uri常量,名称为CONTENT_URI:
public static final UriCONTENT_URI =
Uri.parse("content://com.example.codelab.transportationprovider");
如果有多个表,它们也是使用相同的CONTENT_URI,只是它们的路径部分不同。
也就是说红色框部分是一致的。
定义返回的列名,public static final,列名的值,比如使用SQLite数据库作为存储,对应表的列名。
在文档中要写出各个列的数据类型,便于使用者读取。
如果需要处理新的MIME数据类型,比如通过Intent的方式,并且带data的mimeType(参见总结一下Intent概念),那么需要在ContentProvider.getType()方法中进行处理,参见编写完整的Contentprovider示例编写一个getType方法部分。
如果处理数据库表中超大的数据,比如很大的位图文件,一般存在文件系统中,可以参照在contentprovider中使用大型二进制文件,这样第三方的contentprovider使用者,可以访问不属于它权限的文件,通过contentprovider做代理。
声明 Content Provider
创建ContentProvider后,需要在manifest文件中声明,android系统才能知道它,当其他应用需要调用该ContentProvider时才能创建或者调用它。
语法类似:
<providerandroid:name="com.easymorse.cp.MyContentProvider"
android:authorities="com.easymorse.cp.mycp"></provider>
android:name要写ContentProvider继承类的全名。
android:authorities要写和CONTENT_URI常量的B部分(见上面图)。
注意不要把上图C和D部分加到authorities中去。authorities是用来识别ContentProvider的,C和D部分实际上是ContentProvider内部使用的。