一个ContentProvider 一般是供其它程序访问的,它提供了 跨进程的能力.
一个Provider的表格并不要求必须有一个 _ID 列,不过如果想要把查询的数据在ListView中显示,则必须有 _ID 列.
2 ContentResolver与ContentProvider
Resolver能够解析Uri,根据Uri能够找到其对应的provider,并把query/insert/update/delete操作传递给它,如下图:
![](https://img-my.csdn.net/uploads/201209/20/1348127115_4316.jpg)
ContentResolver的查询方法如下:
public final Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
/*
uri: 指明了ContentProvider的Authority,及此Authority下的表
projection:需要查询哪些列 即 select [projection1,projection2] from table
selection: 即 where a=? and b=?
selectionArgs: 即对应selection语句中的两个 ?
sortOrder:比如为 columnName1 DESC //ASC升序 默认 DESC 降序
*/
一个例子 URI 如下:
content://user_dictionary/words
其中 content 为scheme, user_dictionary 为authory(<provider>自己声明的),words为path(也可理解为表名或单个行)
下面为一个构造Uri的一个小方法:(使 path 部分指向单独的一行)
Uri singleUri = ContentUri.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
3 ContentProvider permission
However, components in the provider's application always have full read and write access, regardless of the specified permissions.
这句话说,和 ContentProvider 在同一个包内的应用程序有所有的权限访问Provider, Provider的permisson对同一个包内的程序不起作用.(这一点在NotePad例子程序中得到体现)
在定义一个 Provider 时需要声明它的permission, 但它的权限声明方法和其它组件不同, 它是使用 <android:readPermission><android:writePermission>及<android:grantUriPermission>, 同时 <permission> 指打开读写权限. 具体可以参考 App Component 中的 Permission 一节(有博客).
4 insert/update/delete 时会用到 ContentValues 类作为一个工具类, 很好理解,不多述.
5 我们知道 MIME 类型在 <intent-filter> 中会用到, 一个 ContentProvider也会声明自己的 MIME 类型,这个 MIME 类型有什么用呢?
文档上说,你的APP可以根据 Provider的MIME类型来决定是否能够解析它的内容,对 Provider 的 MIME 类型的作用还不是很明白! 但有下面一些总结. 你想访问ContentProvider就必须要提供一个 Uri, 对于 ContentProvider来说, 过来一个 Uri 它就会返回一些值, 那么返回的这些值是什么类型的呢? 它应该进行说明一下. 所以 ContentProvider 的一个 Uri就对应一个 MIME 类型. 一些规范的 MIME 类型很少用到, 比如说 text/html, 在 Provider里暂时没有发现这种用法. 但自己确可以定义自己的 MIME 类型, 方法如下:
自定义的 MIME 类型的主类型必须是
vnd.android.cursor.dir //代表多行数据或
vnd.android.cursor.item //代表单行数据而子类型则可以根据表名来定义,下面是一个例子:
Uri :
content://com.example.trains/Line1 //Line1 为一个表的 MIME 为:
vnd.android.cursor.dir/vnd.example.line1 //vnd.example.line1是自己取的名
Uri
content://com.example.trains/Line2/5的MIME为:
vnd.android.cursor.item/vnd.example.line2
6 访问ContentProvider的方法种类
共有三种: batch 访问(略), CursorLoader的异步访问(略), Intent 访问
一个 ContentProvider 并不接受 Intent 的访问方式, 但是和ContentProvider在一个包内的相关APP确可以接受 Intent,通过这个APP,外部的APP就可以得到想要的结果.
这样特定的 Intent 必须包含 FLGA:
FLAG_GRANT_READ_URI_PERMISSION 或 FLAG_GRANT_WRITE_URI_PERMISSION
同时, ContentProvider 也必须通过 <android:grantUriPermission> 或 <grant-permission> 声明 Uri 访问权限.
下面是一个例子:
7 一般情况下, 不必新建 ContentProvider, 你的程序如果需要使用 SQLiteDatabase, 直接使用即可,不用通过 ContentProvider. 只有具有一些供其它程序访问的数据时才需要自己建立一个 ContentProvider. (NotePad 例子应该是个例外,它只是为了展示ContentProvider的用法)
8 建立 ContentProvider 的步骤
1> 确定底层存储方式,是文件还是数据库或其它形式
2> 继承 ContentProvider , 并实现必须的方法
3> 定义好你自己ContentProvider的 authority 及 URI
其实上述步骤并没有先后, 总之就是 先设计好 存储格式(更多是表的格式), 再设计好 Authority, 表的列, 最后实现 ContentProvider子类中的方法.
设计 ContentProvider时 UriMatcher 可以提供帮助, 有两个通配符需要了解:
*: Matches a string of any valid characters of any length.
#: Matches a string of numeric characters of any length.
假设你准备了以下的 URI:
content://com.example.app.provider/table1: A table called table1.
content://com.example.app.provider/table2/dataset1: A table called dataset1.
content://com.example.app.provider/table2/dataset2: A table called dataset2.
content://com.example.app.provider/table3: A table called table3.
则 content://com.example.app.provider/* 匹配所有的 URI, content://com.example.app.provider/table2/* 匹配 dataset1和dataset2,
content://com.example.app.provider/table3/# 匹配 content://com.example.app.provider/table/6 这种形式
下面是 UriMatcher的应用的例子:
sUriMatcher.addURI("com.example.app.provider", "table3", 1);
sUriMatcher.addURI("com.example.app.provider", "table3/#", 2);
switch (sUriMatcher.match(uri)) { // If the incoming URI was for all of table3 case 1: if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC"; break; // If the incoming URI was for a single row case 2: ... }
继承 ContentProvider 的类必须实现的方法有:query( )
insert( )
update( )
delete( )
getType( )
//以上五个方法必须注意多线程问题
onCreate( ) : 此方法应该简洁,以尽快返回,使调用者得到快带的回应(SQLiteOpenHelper会把创建数据库的时间推迟到必须要对数据库进行操作时)
getType( ) 方法特别说明:
自定义 MIME 类型应该是:
vnd.android.cursor.dir/vnd.<name>.<type> 或
vnd.android.cursor.item/vnd.<name>.<type>
其中红色部分为固定不变的 <name>可以为包名, type 可以为表名, 例子如下:
vnd.android.cursor.dir/vnd.com.example.provider.table1
或
vnd.android.cursor.item/vnd.com.example.provider.table1 (for 单独的一行)
MIME for file / getStreamType( ) 暂不学习