ContentProvider的使用

ContentProvider的意义

ContentProvider的类注释如下:

Content providers are one of the primary building blocks of Android applications, providing content to applications. They encapsulate data and provide it to applications through the single ContentResolver interface. A content provider is only required if you need to share data between multiple applications. For example, the contacts data is used by multiple applications and must be stored in a content provider. If you don't need to share data amongst multiple applications you can use a database directly via android.database.sqlite.SQLiteDatabase

内容提供者是Android应用程序的主要构建块之一,为应用程序提供内容。它们封装数据并通过单一的ContentResolver接口提供给应用程序。如果需要在多个应用程序之间共享数据,则只需要内容提供程序。例如,联系人数据由多个应用程序使用,必须存储在内容提供者中。如果您不需要在多个应用程序之间共享数据,您可以直接通过数据库使用数据库。

由此可见,ContentProvider适合进程间的数据共享,你的应用可以通过ContentProvider将需要共享的部分数据提供给其他应用。你也可以通过ContentProvider获取手机的图片、音视频、通讯录等数据

ContentProvider实现

需要重写以下的几个主要方法

  • onCreate 初始化创建ContentProvider
  • query 返回查询数据
  • insert 插入数据
  • update 更新数据
  • delete 删除数据
  • getType

getType方法很特别,基本用不上,但是它具体的作用又是什么呢,来看看它的注释

Implement this to handle requests for the MIME type of the data at the
given URI. The returned MIME type should start with vnd.android.cursor.item for a single record, or vnd.android.cursor.dir/ for multiple items.
根据访问的URI,返回对应的MIME类型的字符串,这个字符串需要以vnd.android.cursor.item(返回单条数据记录)或者vnd.android.cursor.dir/(返回多条数据记录) 开头

看到这些方法是不是很熟悉?跟数据库操作一毛一样啊,其实在这个时候我们可以将ContentProvider理解成一个共享数据库,我们只要在ContentProvider初始化的时候,实例化一个可读写的数据库,在以上的方法中对这个数据库进行增删改查的操作就可以了。当你的数据库有多个表的时候,这些增删改查又是如何区分不同的表呢?其实这些方法都有一个Uri的参数回调,我们可以通过实例UriMatcher来区分用户操作的到底是哪个表。

示例

创建一个DBOpenHelper


public static class TestDBOpenHelper extends SQLiteOpenHelper{
    public static final String DB_NAME = "test.db";
    public static final int VERSION=1;
    public static final String TABLE_NAME = "person";
    public static final String TABLE_NAME2 = "class";
    public static final String CREATE_TABLE = "create table if not exists "+TABLE_NAME+"(_id integer, name TEXT, desc TEXT)";
    public static final String CREATE_TABLE2= "create table if not exists "+TABLE_NAME2+"(id integer, className TEXT, desc TEXT)";

    public TestDBOpenHelper(Context context){

        super(context,DB_NAME,null,VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {

        db.execSQL(CREATE_TABLE);
        db.execSQL(CREATE_TABLE2);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

ContentProvider


public class TestContentProvider extends ContentProvider {

        private static final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        public static final String AUTHORITY = "com.gzkit.dailysample.TestContentProvider";  //授权
        public static final Uri PERSON_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/person");
        public static final Uri CLASS_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/class");

        private static final int TABLE_CODE_PERSON = 2;
        private static final int TABLE_CODE_CLASS = 3;

        static {
            //关联不同的 URI 和 code,便于后续 getType
            mUriMatcher.addURI(AUTHORITY, "person", TABLE_CODE_PERSON);
            mUriMatcher.addURI(AUTHORITY, "class", TABLE_CODE_CLASS);
        }

            //数据库
        private SQLiteDatabase mDatabase;
        private Context mContext;
        private String mTable;

        private void initProvider(){

            mContext = getContext();
            mTable = TestDBOpenHelper.TABLE_NAME;
            mDatabase = new TestDBOpenHelper(mContext).getWritableDatabase();
                //插入一条初始数据
            mDatabase.execSQL("insert into "+mTable+" values(1,'test','good boy') ");


        }


        @Override
        public boolean onCreate() {

            initProvider();
            return false;
        }

        @Nullable
        @Override
        public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {

            String tableName = getTableName(uri);


            return mDatabase.query(tableName,projection,selection,selectionArgs,null,sortOrder,null);
        }

        @Nullable
        @Override
        public String getType(@NonNull Uri uri) {
            int match = mUriMatcher.match(uri);
            String MIME = "";
            switch (match) {
                case TABLE_CODE_PERSON:

                    MIME = "vnd.android.cursor.dir/multi";
                    break;
                case TABLE_CODE_CLASS:
                    MIME = "vnd.android.cursor.item/single";

                    break;
            }

            return MIME;
        }

        @Nullable
        @Override
        public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
            String tableName = getTableName(uri);

            mDatabase.insert(tableName,null,values);
            //发送数据变动的通知
            mContext.getContentResolver().notifyChange(uri,null);
            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;
        }

        /**
         * CRUD 的参数是 Uri,根据 Uri 获取对应的表名
         *
         * @param uri
         * @return
         */
        private String getTableName(final Uri uri) {
            String tableName = "";
            int match = mUriMatcher.match(uri);
            switch (match){
                case TABLE_CODE_PERSON:
                    tableName = TestDBOpenHelper.TABLE_NAME;

                    break;

                case TABLE_CODE_CLASS:
                    tableName = TestDBOpenHelper.TABLE_NAME2;

                    break;
            }
            return tableName;
        }

在AndroidManifest中注册ContentProvider

提供ContentProvider的APP按实际需要加入以下权限




如果provider不存在这些权限的属性,声明这些权限则不是必要的

application节点下




  • name 自定义的类
  • authorities 定义授权的名称,在自定义的ContentProvider中要用到
  • exported 外部是否能调用
  • process 进程名
  • readPermission 读取的权限名,必须和前面的permission节点名一致
  • writePermission 写入的权限名,必须和前面的permission节点名一致
  • permission 读写的权限名,必须和前面的permission节点名一致

值得注意的是,readPermission和writePermission的优先级比permission高。

有以下几种情况

  • 所有权限都不声明,其他应用无须声明权限就可以自由读写
  • 只声明了permission,则其他应用需要声明对应的权限,即可读写
  • 只声明readPermission,则其他应用需要声明此权限,才可以读取数据,但仍然可以写入数据
  • 只声明writePermission,和上面相反
  • 声明readPermission或writePermission,同时声明了permission,则其他应用需要声明对应的权限,可以读写。

ContentProvider声明了哪种权限,调用的那方就必须声明对应的权限

调用方需要声明对应的权限





使用ContentProvider

在activity或service中通过getContentResolver()获取resolver接口进行数据的增删查改


public static final String AUTHORITY = "com.gzkit.dailysample.TestContentProvider";  //授权
public static final Uri PERSON_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/person");
public static final Uri CLASS_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/class");


public void insert(){
        ContentValues contentValues = new ContentValues();
        contentValues.put("_id",1);
        contentValues.put("name","test here");
        contentValues.put("desc","here is desc");
        getContentResolver().insert(PERSON_CONTENT_URI,contentValues);
}

public void query(){
        Cursor cursor =  getContentResolver().query(CLASS_CONTENT_URI,null,null,null,null);
        Toast.makeText(mContext, "查询结果数:"+cursor.getCount(), Toast.LENGTH_SHORT).show();
        cursor.close();
}

下一篇,将会讲到如何利用ContentProvider读取手机的多媒体资料

你可能感兴趣的:(ContentProvider的使用)