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..
    • 如:
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进行声明

        
        
  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;
        }
    }

你可能感兴趣的:(Content Provider笔记)