Content Provider笔记

参考:

一. 用途:

  • 跨程序共享数据(为其他应用程序提供访问数据的接口)
  • 利用Content Provider对数据进行封装,有利于脱离对数据库的依赖性(解耦)。改变底层数据库,而上层数据查询不用改变。

二. 用到的各种类

  1. Content Resolver

用来访问ContentProvider的类,提供了和ContentProvider类中方法同名的方法,Content Provider只是用来提供数据的,而访问就需要用Content Resolver。

  1. Content Provider

内容提供器,可以用来给其他程序提供数据,也可以充当其数据存储区和表格形式的数据外部显示之间的抽象层。主要用来提供对外访问的接口(CRUD)。

  1. 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的作用有很多,不仅仅是用在这里,还有如用浏览器打开链接的时候会用到
  1. 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参数
  1. 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
  1. 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)
  1. Uri的MIME类型(不知道是干嘛的,原谅我还没学计算机网络QAQ)
    • 构成方法:
      • 以vnd开头
      • 如URI以路径结尾,则接android.cursor.dir,如果以id结尾,则接android.cursor.item
      • 接着再接/vnd.<authority>.<path>
    • 如:
vnd.android.cursor.dir/cn.foxnickel.databasedemo.provider.book//路径结尾
vnd.android.cursor.item/cn.foxnickel.databasedemo.provider.book//id结尾

三. 访问Content Provider提供的数据的步骤

  1. 构建要访问的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
  1. 得到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

  1. 实现自己的Content Provider类,继承自Content Provider基类
  2. 实现六个必须的抽象方法(onCreate,getType,insert,delete,update,query)
  3. 实例化UriMatcher,添加要提供的Uri
  4. 根据添加的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;
    }
}
  1. manifest进行声明
<!--
            name:Content Provider的名字
            authorities:权限(包名)
            enable:是否启用
            exported:是否能被其他软件使用
        -->
        <provider
            android:name=".data.DatabaseProvider"
            android:authorities="cn.foxnickel.databasedemo.provider"
            android:enabled="true"
            android:exported="true">
        </provider>
  1. 创建完成之后就可以通过三中的调用方法进行调用了

五. 源码

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;
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容