欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ContentResolver与ContentProvider的搭配使用

程序员文章站 2024-02-09 16:54:04
...

简介

Android中, ContentResolver是通过Uri来获取对应数据的数据, 而提供数据的,通常就是ContentProvider。ContentProvider可以作为一个公共的数据源,为多个app提供数据的增删改查。

以下例子先建立一个ContentProvider, 在其内部简历数据库,然后让ContentResolver来查询数据。

先看例子的示意图:
1、插入数据和查询数据
查看数据库的两列数据:1、单词; 2、单词描述, 这里直插入了单词,暂未插入描述
ContentResolver与ContentProvider的搭配使用
2、修改数据
修改以b结尾的单词的描述, 这里把描述改为了222

ContentResolver与ContentProvider的搭配使用

3、删除数据
删除以a开头的所有单词
ContentResolver与ContentProvider的搭配使用
4、数据库表(用Sqlite Expert工具查看)
主要有三列: id, word, describe
ContentResolver与ContentProvider的搭配使用
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