ContentResolver与ContentProvider的搭配使用

简介

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值