参考:
- 内容提供程序基础知识
- 创建内容提供程序
一. 用途:
- 跨程序共享数据(为其他应用程序提供访问数据的接口)
- 利用Content Provider对数据进行封装,有利于脱离对数据库的依赖性(解耦)。改变底层数据库,而上层数据查询不用改变。
二. 用到的各种类
- Content Resolver
用来访问ContentProvider的类,提供了和ContentProvider类中方法同名的方法,Content Provider只是用来提供数据的,而访问就需要用Content Resolver。
- Content Provider
内容提供器,可以用来给其他程序提供数据,也可以充当其数据存储区和表格形式的数据外部显示之间的抽象层。主要用来提供对外访问的接口(CRUD)。
- Uri
用来区分应用程序、字段的URI,与RUL类似,分为一下三个部分,要想使用Content Provider提供的数据,就必须通过Uri进行访问,而且只能通过在ContentProvider中已经添加(调用UriMatcher.addURI添加的)的Uri访问,未添加的就不能访问。
- schema:协议部分,Content Provider默认协议是content://
- authority:权限部分,其实就是用来区分是哪个Content Provider,通常是应用的包名
- path:用来区分数据的路径,一般是表名,有以下形式:
- 访问多条数据(整个表)com.example.provider/table
- 访问某一行(rowId对应的行)com.example.provider/table/rowId
- PS:Uri的作用有很多,不仅仅是用在这里,还有如用浏览器打开链接的时候会用到
- Uri.Builder
- 用来连缀构建标准Uri的类(调用各种方法进行参数的连缀),有如下常用方法:(其实这些方法很多都是重复的功能)
Uri.Builder scheme(String scheme)//给Uri添加schema参数
Uri.Builder authority(String authority)//给Uri添加authority参数
Uri.Builder path(String path)//给Uri添加path参数
Uri.Builder query(String query)//给Uri添加id参数(查询参数)
Uri build()//生成Uri
Uri.Builder appendPath(String newSegment)//添加path参数
- ContentUris
- 用来给Uri追加id和读取Uri中的id的帮助类,有如下方法:
static Uri.Builder appendId(Uri.Builder builder, long id)//给Uri.Builder要构建的Uri添加id
static long parseId(Uri contentUri)//解析Uri中的id
static Uri withAppendedId(Uri contentUri, long id)//给Uri添加d
- UriMatcher
- Content Provider用来处理(匹配)不同Uri的类,创建Content Provider的时候就依靠UriMatcher进行匹配从而确定对不同的Uri请求进行不同的操作,有以下两个方法:
/**
* 添加一个要匹配的Uri,可以用通配符,其中*用来通配一个字符,#用来匹配任何数字。
*
* @param authority Uri的authority
* @param 要匹配的路径,可以用通配符
* @param 匹配成功时返回的数字,根据该数字进行判断和匹配
*/
public void addURI(String authority, String path, int code)
/**
* 进行匹配的方法
*
* @param uri 要匹配的Uri
*
* @return 返回值,匹配到时返回addURI里面的第三个参数,匹配失败(Content Provider未提供该uri)的时候返回-1
*/
public int match(Uri uri)
- Uri的MIME类型(不知道是干嘛的,原谅我还没学计算机网络QAQ)
- 构成方法:
- 以vnd开头
- 如URI以路径结尾,则接android.cursor.dir,如果以id结尾,则接android.cursor.item
- 接着再接/vnd.
.
- 如:
- 构成方法:
vnd.android.cursor.dir/cn.foxnickel.databasedemo.provider.book//路径结尾
vnd.android.cursor.item/cn.foxnickel.databasedemo.provider.book//id结尾
三. 访问Content Provider提供的数据的步骤
- 构建要访问的Uri
- 调用Uri.parse()将String转化为Uri
- 调用Uri.Builder方法进行各种连缀构造(其实可以不用调用这些方法,直接写好String然后parse成Uri就可以了,但是推荐做法是调用API中的函数进行构造),一般有如下方式构建Uri:(构建Uri的方法很多,这里只是两种)
Uri uri = new Uri.Builder().scheme("content").authority("cn.foxnickel.databasedemo.provider").path("table1").build();//用Builder进行构造
Uri uri1 = Uri.parse("content://cn.foxnickel.databasedemo.provider/table/id");//用String构建Uri
- 得到Content Resolver并调用CRUD方法进行数据的操作
- 得到Content Resolver(Context提供了getContentResolver方法用来得到Content Resolver)
ContentResolver contentResolver = Context.getContentResolver();
- 调用Content Resolver的CRUD方法进行数据操作,和SQLiteDatabase的CRUD方法一样。
三. 访问系统提供的Content Provider
- 访问系统提供的Content Provider的时候就不需要自己构建Uri,因为系统对各种Uri都是封装好了的,只需要查找官方文档就可以使用了
- 常见的内置Content Provider
- 联系人提供程序
- 日历提供查询
- 短信提供程序
- 用户字典提供程序
- 媒体提供程序
四. 创建自己应用的Content Provider
- 实现自己的Content Provider类,继承自Content Provider基类
- 实现六个必须的抽象方法(onCreate,getType,insert,delete,update,query)
- 实例化UriMatcher,添加要提供的Uri
- 根据添加的Uri进行判断,然后具体实现六个抽象方法
/**
* 该类是ContentProvider的实现类,实现了对数据库数据的共享
*/
public class DatabaseProvider extends ContentProvider {
/**
* 定义支持的Uri的匹配号
*/
private static final int BOOK_DIR = 0;
private static final int BOOK_ITEM = 1;
private static final int CATEGORY_DIR = 2;
private static final int CATEGORY_ITEM = 3;
private static final String AUTHORITY = "cn.foxnickel.databasedemo.provider";
private static UriMatcher mUriMatcher;//静态的UriMatcher
private MyDatabaseHelper mDatabaseHelper;//用来查询数据库数据的Helper
private final String TAG = getClass().getSimpleName();
private SQLiteDatabase db;//数据库
/**
* 在静态代码块中进行Uri的添加
*/
static {
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mUriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
mUriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
mUriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
mUriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
}
/**
* delete方法:删除数据库中的数据
* @param uri 指定表或者行的Uri
* @param selection 删除的条件
* @param selectionArgs selection中占位符的值
* @return 返回删除的某行的行号
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.i(TAG, "content provider delete:" + uri);
int deletedRows = 0;
switch (mUriMatcher.match(uri)) {
case BOOK_DIR:
deletedRows = db.delete("book",selection,selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
deletedRows = db.delete("book","id = ?",new String[]{bookId});
break;
case CATEGORY_DIR:
deletedRows = db.delete("category",selection,selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
deletedRows = db.delete("category","id = ?",new String[]{categoryId});
break;
default:
break;
}
return deletedRows;
}
/**
* 返回传入Uri的MIME类型
* @param uri ContentResolver传来的Uri
* @return 该Uri对应的MIME类型
*/
@Override
public String getType(Uri uri) {
Log.i(TAG, "content provider getType:" + uri);
switch (mUriMatcher.match(uri)) {
case BOOK_DIR:
return "vnd.android.cursor.dir/cn.foxnickel.databasedemo.provider.book";
case BOOK_ITEM:
return "vnd.android.cursor.item/cn.foxnickel.databasedemo.provider.book";
case CATEGORY_DIR:
return "vnd.android.cursor.dir/cn.foxnickel.databasedemo.provider.category";
case CATEGORY_ITEM:
return "vnd.android.cursor.item/cn.foxnickel.databasedemo.provider.category";
default:
break;
}
return null;
}
/**
* 插入数据
* @param uri 指定表或者行的Uri
* @param values 包装了要插入数据的ContentValues
* @return 用于表示新纪录的Uri
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.i(TAG, "content provider insert:" + uri);
Uri uriReturn = null;
switch (mUriMatcher.match(uri)) {
case BOOK_DIR:
case BOOK_ITEM:
long newBookId = db.insert("book",null,values);
uriReturn = Uri.parse("content://"+AUTHORITY+"/book/"+newBookId);
break;
case CATEGORY_DIR:
case CATEGORY_ITEM:
long newCategoryId = db.insert("category",null,values);
uriReturn = Uri.parse("content://"+AUTHORITY+"/category/"+newCategoryId);
break;
default:
break;
}
return uriReturn;
}
/**
* 当ContentProvider第一次被调用的时候会调用onCreate方法,一般在这里进行数据库的初始化
* @return 是否初始化成功
*/
@Override
public boolean onCreate() {
Log.i(TAG, "content provider onCreate: ");
mDatabaseHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
db = mDatabaseHelper.getWritableDatabase();
return true;
}
/**
* 查询数据
* @param uri 指定要查询的表/列的Uri
* @param projection 要查询的列
* @param selection where条件
* @param selectionArgs selection条件中占位符的值
* @param sortOrder 排序方式,默认为升序
* @return 查询到的游标
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
Cursor cursor = null;
switch (mUriMatcher.match(uri)) {
case BOOK_DIR:
Log.i(TAG, "content provider query:" + uri);
cursor = db.query("book", projection, selection, selectionArgs, null, null, sortOrder);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
cursor = db.query("book", projection, "id = ?", new String[]{bookId}, null, null, sortOrder);
break;
case CATEGORY_DIR:
cursor = db.query("category", projection, selection, selectionArgs, null, null, sortOrder);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
cursor = db.query("category", projection, "id = ?", new String[]{categoryId}, null, null, sortOrder);
break;
default:
break;
}
return cursor;
}
/**
* 更新数据
* @param uri 指定要更新的表/列的Uri
* @param values 包装了要更新数据的ContentValues
* @param selection where条件
* @param selectionArgs selection条件中占位符的值
* @return 更新的行号
*/
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
Log.i(TAG, "content provider update:" + uri);
int updatedRows = 0;
switch (mUriMatcher.match(uri)) {
case BOOK_DIR:
updatedRows = db.update("book",values,selection,selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
updatedRows = db.update("book",values,"is = ?",new String[]{bookId});
break;
case CATEGORY_DIR:
updatedRows = db.update("category",values,selection,selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
updatedRows = db.update("book",values,"is = ?",new String[]{categoryId});
break;
default:
break;
}
return updatedRows;
}
}
- manifest进行声明
- 创建完成之后就可以通过三中的调用方法进行调用了
五. 源码
Provider类
/**
* 该类是ContentProvider的实现类,实现了对数据库数据的共享
*/
public class DatabaseProvider extends ContentProvider {
/**
* 定义支持的Uri的匹配号
*/
private static final int BOOK_DIR = 0;
private static final int BOOK_ITEM = 1;
private static final int CATEGORY_DIR = 2;
private static final int CATEGORY_ITEM = 3;
private static final String AUTHORITY = "cn.foxnickel.databasedemo.provider";
private static UriMatcher mUriMatcher;//静态的UriMatcher
private MyDatabaseHelper mDatabaseHelper;//用来查询数据库数据的Helper
private final String TAG = getClass().getSimpleName();
private SQLiteDatabase db;//数据库
/**
* 在静态代码块中进行Uri的添加
*/
static {
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mUriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
mUriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
mUriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
mUriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
}
/**
* delete方法:删除数据库中的数据
* @param uri 指定表或者行的Uri
* @param selection 删除的条件
* @param selectionArgs selection中占位符的值
* @return 返回删除的某行的行号
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.i(TAG, "content provider delete:" + uri);
int deletedRows = 0;
switch (mUriMatcher.match(uri)) {
case BOOK_DIR:
deletedRows = db.delete("book",selection,selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
deletedRows = db.delete("book","id = ?",new String[]{bookId});
break;
case CATEGORY_DIR:
deletedRows = db.delete("category",selection,selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
deletedRows = db.delete("category","id = ?",new String[]{categoryId});
break;
default:
break;
}
return deletedRows;
}
/**
* 返回传入Uri的MIME类型
* @param uri ContentResolver传来的Uri
* @return 该Uri对应的MIME类型
*/
@Override
public String getType(Uri uri) {
Log.i(TAG, "content provider getType:" + uri);
switch (mUriMatcher.match(uri)) {
case BOOK_DIR:
return "vnd.android.cursor.dir/cn.foxnickel.databasedemo.provider.book";
case BOOK_ITEM:
return "vnd.android.cursor.item/cn.foxnickel.databasedemo.provider.book";
case CATEGORY_DIR:
return "vnd.android.cursor.dir/cn.foxnickel.databasedemo.provider.category";
case CATEGORY_ITEM:
return "vnd.android.cursor.item/cn.foxnickel.databasedemo.provider.category";
default:
break;
}
return null;
}
/**
* 插入数据
* @param uri 指定表或者行的Uri
* @param values 包装了要插入数据的ContentValues
* @return 用于表示新纪录的Uri
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.i(TAG, "content provider insert:" + uri);
Uri uriReturn = null;
switch (mUriMatcher.match(uri)) {
case BOOK_DIR:
case BOOK_ITEM:
long newBookId = db.insert("book",null,values);
uriReturn = Uri.parse("content://"+AUTHORITY+"/book/"+newBookId);
break;
case CATEGORY_DIR:
case CATEGORY_ITEM:
long newCategoryId = db.insert("category",null,values);
uriReturn = Uri.parse("content://"+AUTHORITY+"/category/"+newCategoryId);
break;
default:
break;
}
return uriReturn;
}
/**
* 当ContentProvider第一次被调用的时候会调用onCreate方法,一般在这里进行数据库的初始化
* @return 是否初始化成功
*/
@Override
public boolean onCreate() {
Log.i(TAG, "content provider onCreate: ");
mDatabaseHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
db = mDatabaseHelper.getWritableDatabase();
return true;
}
/**
* 查询数据
* @param uri 指定要查询的表/列的Uri
* @param projection 要查询的列
* @param selection where条件
* @param selectionArgs selection条件中占位符的值
* @param sortOrder 排序方式,默认为升序
* @return 查询到的游标
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
Cursor cursor = null;
switch (mUriMatcher.match(uri)) {
case BOOK_DIR:
Log.i(TAG, "content provider query:" + uri);
cursor = db.query("book", projection, selection, selectionArgs, null, null, sortOrder);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
cursor = db.query("book", projection, "id = ?", new String[]{bookId}, null, null, sortOrder);
break;
case CATEGORY_DIR:
cursor = db.query("category", projection, selection, selectionArgs, null, null, sortOrder);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
cursor = db.query("category", projection, "id = ?", new String[]{categoryId}, null, null, sortOrder);
break;
default:
break;
}
return cursor;
}
/**
* 更新数据
* @param uri 指定要更新的表/列的Uri
* @param values 包装了要更新数据的ContentValues
* @param selection where条件
* @param selectionArgs selection条件中占位符的值
* @return 更新的行号
*/
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
Log.i(TAG, "content provider update:" + uri);
int updatedRows = 0;
switch (mUriMatcher.match(uri)) {
case BOOK_DIR:
updatedRows = db.update("book",values,selection,selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
updatedRows = db.update("book",values,"is = ?",new String[]{bookId});
break;
case CATEGORY_DIR:
updatedRows = db.update("category",values,selection,selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
updatedRows = db.update("book",values,"is = ?",new String[]{categoryId});
break;
default:
break;
}
return updatedRows;
}
}
调用Provider的类
public void onClick(View v) {
ContentResolver contentResolver = getContentResolver();
switch (v.getId()) {
case R.id.add_data:
ContentValues values = new ContentValues();
values.put("name","A Clash of Kings");
values.put("author","George Martin");
values.put("pages",1040);
values.put("price",22.85);
Uri newUri = contentResolver.insert(Uri.parse(AUTHORITY+"/book"),values);
newId = newUri.getPathSegments().get(1);
break;
case R.id.query_data:
Cursor cursor = null;
cursor = contentResolver.query(Uri.parse(AUTHORITY+"/book"),null,null,null,null);
if(cursor!=null){
while (cursor.moveToNext()){
Log.i(TAG, "onClick: name: "+cursor.getString(cursor.getColumnIndex("name")));
Log.i(TAG, "onClick: author: "+cursor.getString(cursor.getColumnIndex("author")));
Log.i(TAG, "onClick: price: "+cursor.getFloat(cursor.getColumnIndex("price")));
Log.i(TAG, "onClick: pages: "+cursor.getString(cursor.getColumnIndex("pages")));
}
cursor.close();
}
break;
case R.id.update_data:
ContentValues newValues = new ContentValues();
newValues.put("name","A Storm of sWords");
contentResolver.update(Uri.parse(AUTHORITY+"/book"),newValues,"pages = ?",new String[]{"1040"});
break;
case R.id.delete_data:
contentResolver.delete(Uri.parse(AUTHORITY+"/book/"+1),null,null);
break;
default:
break;
}
}