简介
Android中, ContentResolver是通过Uri来获取对应数据的数据, 而提供数据的,通常就是ContentProvider。ContentProvider可以作为一个公共的数据源,为多个app提供数据的增删改查。
以下例子先建立一个ContentProvider, 在其内部简历数据库,然后让ContentResolver来查询数据。
先看例子的示意图:
1、插入数据和查询数据
查看数据库的两列数据:1、单词; 2、单词描述, 这里直插入了单词,暂未插入描述
2、修改数据
修改以b结尾的单词的描述, 这里把描述改为了222
3、删除数据
删除以a开头的所有单词
4、数据库表(用Sqlite Expert工具查看)
主要有三列: id, word, describe
Sqlite Expert工具破解版下载地址: https://download.csdn.net/download/qq475703980/11833576
一、ContentProvider的创建
1、自定义ContentProvider
继承ContentProvider后,需要实现六个抽象方法:
public class DictProvider extends ContentProvider {
@Override
public boolean onCreate() {
//只会在ContentProvider启动时调用一次
return true;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
}
下面就介绍具体这几个方法如何使用。
Uri简介
Uri: 统一资源标识符(Uniform Resource Identifier,URI)。每个资源都应该有唯一一个符号来标识,就像每一个网址都有唯一可识别的地址一样。
我们先看当前博客的网址:https://blog.csdn.net/qq475703980
这个地址可以分为三部分:
- 1、https:// :这个表示协议,只能通过https协议来访问,这部分是固定的
- 2、blog.csdn.net : 域名部分,这是CSDN的网址
- 3、qq475703980 :网址资源部分,这里就指向了CSDN的qq475703980这个博客
在android中,我们provider的uri地址通常格式为:“content://com.op.billy.dict.provider/dict”, 其中“content://”是固定的协议,而"com.op.billy.dict.provider"是一个provider的唯一标识,这个也需要在manifest中注册 "dict"标识这个provider里面的某个资源路径。
在Manifest中注册ContentProvider
<provider
android:name=".DictProvider"
android:authorities="com.op.billy.dict.provider"
android:exported="true" />
authorities是用来一个Provider的唯一标识,就类似于网站的网址一样,每个网站的网址是固定的,不会两家公司的网址都是相同的,所以在Android中,为了防止两个应用的authorities相同, 如果后安装的apk的authorities和已安装的apk的authorities相同,则新的apk无法安装成功。因为apk安装时,会首先解析apk的manifest,然后将其中的四大组件注册到系统服务中,进行统一管理,先安装的apk自然就先注册了。
1、onCreate方法
onCreate方法只会在provider启动时调用一次,启动后不再回调此方法,通常用来做一些初始化操作及拿到DatabaseHelper的引用:
@Override
public boolean onCreate() {
Log.d(TAG, "onCreate");
//初始化操作dict_db这个数据库的DatabaseHelper,自定义的DictDatabaseHelper主要是用来创建了数据库dict_db
mDbHelper = new DictDatabaseHelper(getContext(), "dict_db", null, 1);
return true;
}
2、getType方法
getType主要是用来返回数据mimetype类型:
@Override
public String getType(@NonNull Uri uri) {
//返回指定Uri参数对应的数据的MIME TYPE 类型
switch (mUriMatcher.match(uri)) {
case CODE_DICT:
//返回的cursor是单项记录,即只查询了一行
return "vnd.android.cursor.item/op.dict";
case CODE_DESCRIBE:
return "vnd.android.cursor.item/op.describe";
case CODE_DICTS:
//返回的cursor是多想记录,查询了多行
return "vnd.android.cursor.dir/op.dicts";
case CODE_DESCRIBES:
return "vnd.android.cursor.dir/op.describes";
}
return null;
}
这个方法的意义是返回某种mime类型,确定了类型以后,去查询数据库的时候就不会再进行判断了, 效率更高;如果这里返回任意字符串也是可以的,只是这个方法就是去了作用,查询数据库的效率会降低。
这里根据传递进来的uri,返回不同的mime类型, 其中"vnd.android.cursor.item/op.dict"表示cursor操作单行记录录,"vnd.android.cursor.dir/op.dicts"表示 cursor操作多行记录。
其中前半段"vnd.android.cursor.dir"是Android系统定义的mime类型,是系统可以识别的,“vnd.android.cursor.dir” 表示cursor可以操作的是多行数据; 后半段"/op.dicts"使我们自定义的字段,表示具体的我们的provider内部使用的一种类型。
系统定义了很多mime类型,我们在manifest清单中设置四大组件有时也会使用到, 具体的mime类型介绍可以参考博客: https://blog.csdn.net/harvic880925/article/details/44620851
3、insert方法
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
SQLiteDatabase db = mDbHelper.getWritableDatabase();
switch (mUriMatcher.match(uri)) {
case CODE_DICT:
long rowId = db.insert(DictDatabaseHelper.TABLE_DICT, DictDatabaseHelper.COLUMN_WORD, values);
if(rowId > 0) {
Log.d(TAG, "rowId = "+rowId);
Uri wordUri = ContentUris.withAppendedId(uri, rowId);
return wordUri;//返回这个插入单词的位置
notifyDataChanged(uri);
}
}
return null;
}
这里主要介绍参数ContentValues, ContentValue主要通过key ,value来保存值,其key应该与数据库表中的列名相同, value就是这一列需要插入的值, 如果key与列名不对应,会报错。
4、query方法
执行数据库的查询方法,
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
Log.d(TAG, "query");
SQLiteDatabase db = mDbHelper.getReadableDatabase();
switch (mUriMatcher.match(uri)) {
case CODE_DICT:
Log.d(TAG, "CODE_DICT");
//执行查询
return db.query(DictDatabaseHelper.TABLE_DICT, projection, selection, selectionArgs, null, null, sortOrder);
default:
}
return null;
}
它的参数分别为:
- Uri uri: 根据传递过来的uri, 我们去查询数据库中不同的表,uri和表的对应关系,使我们自己定义的;
- String[] projection: 要查询的列的名字 组成的数组,如果有数据,返回的cursor数据也会按照这个projection传递进来的列名的顺序返回对应的数据;
- String selection: 筛选条件,如果需要对数据进行某种筛选
- String[] selectionArgs: 如果上一条 selection是一个完整的sql筛选语句,则此参数可以为null, 如果不是一个完整的selection语句,其具体参数用占位符?替代了,那么这个参数依次传递的数据替代前一句的占位符?;
- String sortOrder:数据库的排序语句, 可以为null;
剩下的delete和update方法就和上述方法类似了,传入的参数已经介绍过了, 下面看ContentResolver如何来操作我们的Provider,使其可以增删改查数据库的数据。
二、使用ContentResolver操作ContentProvider的数据
只要有Context上下文,我们就可以通过它来获取ContentResolver
ContentResolver mResolver = context.getContentResolver();
1、使用ContentResolver插入数据
private final String AUTHORITY = "com.op.billy.dict.provider";
private final String DICT_PATH = "dict";
private final String COLUMN_NAME_DICT = "word";
private final String COLUMN_NAME_DESCRIBE = "describe";
private final Uri URI_DICT = Uri.parse("content://" + AUTHORITY + File.separatorChar + DICT_PATH);
...
ContentValues cv = new ContentValues();
cv.put(COLUMN_NAME_DICT, input);//根据列名插入数据
mResolver.insert(URI_DICT, cv);
使用ContentResolver修改更新数据
String input_describe = mEditText.getText().toString();
cv.put(COLUMN_NAME_DESCRIBE,input_describe);
String where = "word like ?";//"word like '%b'";//如果使用?做占位符,那么下面再传入模糊查询的字符串时,不在需要单引号
String[] selectionArgs_update = {"%b"};
mResolver.update(URI_DICT_DESCRIBE, cv, where, selectionArgs_update);
注意坑点, 这里"word like ‘%b’"表示筛选的sql语句,指筛选word这一列中以b结尾的单词,注意如果直接在where语句中这么写, %b 要加单引号, 如果使用下一种占位符的方式传递,则不需要单引号:
String where = "word like ?"
String[] selectionArgs_update = {"%b"};
###使用ContentResolver修改获取数据
//projection :想要查询的列的的集合(传入列名),可以使单列或多列
String selection = "word like '%b'"; //查询的选择条件, 具体值用占位符 ? 表示
//查询条件中的具体值,代替上述的占位符, a%表示以a开头, %b表示以b结尾, %c%表示包含c
String[] selectionArgs = {" a% ", " %b "};
//查询单词以a开头的,描述以b结尾的 行
Cursor cursor = mResolver.query(URI_DICT, mProjections, null, null, null);
if(cursor != null &&cursor.getCount() > 0) {
int count = cursor.getCount();
String result = "result count= " + count +"\n";
while(cursor.moveToNext()) {
result += cursor.getString(0) +", " +cursor.getString(1)+"\n";
}
mTvShow.setText(result);
##demo 源码
以上就是关于ContentProvider以及ContentResolver的使用, demo的源码地址如下,环宇查看:
https://github.com/Billyshi/BillyApplication.git
https://github.com/Billyshi/BillyProvider.git