android基础-contentProvider

知识点

  1. uri类
  2. ContentProvider,ContentResolver
  3. 实现跨进程访问数据

一、Uri类

  1. uri定义:Uniform Resource Identifier,即统一资源标识符
  2. uri作用:外界进程通过 uri找到对应的contentProvider& 其中的数据,再进行数据操作
  3. uri详解:
    uri分为 系统预置 & 自定义,分别对应系统内置的数据(如通讯录、日程表等等)和自定义数据库
    自定义的一般格式如下图:

    《android基础-contentProvider》 图片来自网络.png

    自定义uri的栗子

设置URI
Uri uri = Uri.parse("content://com.returnTolife.provider/test/1") ;

上述URI指向的资源是:名为 `com.returnTolife.provider`的`ContentProvider` 中表名 为`test` 中的 `id`为1的数据

特别注意:URI模式存在匹配通配符* & #

*:匹配任意长度的任何有效字符的字符串
以下的URI 表示 匹配provider的任何内容
"content://com.returnTolife.provider/* "

#:匹配任意长度的数字字符的字符串
以下的URI 表示 匹配provider中的test表的所有行
"content://com.returnTolife.provider/test/# "

系统常用URI

二、ContentProvider和ContentResolver

  1. 关于ContentProvider:由于进程间共享数据的本质是:添加、删除、获取 & 修改(更新)数据,所以ContentProvider 其中的核心方法主要如下:
该方法在ContentProvider被创建后调用,当其他应用程序第一次访问ContentProvider时,该ContentProvider被创建。
public boolean onCreate():

根据Uri查询出selection条件所匹配的全部记录
public Cursor query():

该方法返回当前Uri所代表的数据的MIME类型。如果该Uri对应的数据可能包括多条记录,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,单条记录则为vnd.android.cursor.item/开头;
个人理解该方法返回的MIME在android主要是用来做页面跳转
public String getType()

根据该Uri插入values对应的数据。
public Uri insert():

根据Uri删除selection条件所匹配的全部记录。
public int delete():

根据Uri修改selection条件所匹配的全部记录
public int update():

1.1 关于getType()的作用可以参考ContentProvider数据库共享之——MIME类型与getType() ,文章中写得很详细,而且清晰易懂.
1.2 ContentProvider类并不会直接与外部进程交互,而是通过ContentResolver 类

  1. 关于ContentResolver :外部进程通过 ContentResolver类 从而与ContentProvider类进行交互,原因是:如果一款应用要使用多个ContentProvider,若需要了解每个ContentProvider的不同实现从而再完成数据交互,操作成本高并且难度会比较大所以再ContentProvider类上加多了一个 ContentResolver类对所有的ContentProvider进行统一管理。
    使用栗子
ContentResolver resolver =  getContentResolver(); 

Uri uri = Uri.parse("content://com.returnTolife.provider/test/"); 

Cursor cursor = resolver.query(uri, null, null, null, null); 
//其他方法类似,不重复
  1. UriMatcher类:该类为contentProvider的辅助类,主要用于将一个uri地址与provider进行绑定
    栗子
  public static final int TABLE_STUDENT_CODE = 1;
  public static final int TABLE_BOOKE_CODE = 2;

static{
     //参数表示初始化时不匹配任何东西
     UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); 

      //matcher .addURI的作用是在后面使用matcher.match(string s)匹配的时候,如果匹配到/student"时返回TABLE_STUDENT_CODE即数值1,匹配“/book”时返回MATCH_SECOND
     matcher .addURI(AUTHORITY, "student", TABLE_STUDENT_CODE);
     matcher .addURI(AUTHORITY, "book", TABLE_BOOKE_CODE)
}


三、实例

  1. 数据准备,先创建一个数据库
public class MyDbHelper extends SQLiteOpenHelper {


    private static final String DB_NAME="myprovider.db";

    public static final String TABLE_SUTEMT="student";
    public static final String TABLE_BOOK="book";

    private static final int VERSION=1;

    public MyDbHelper(Context context) {
        super(context, DB_NAME, null, VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_SUTEMT + "(_id INTEGER PRIMARY KEY AUTOINCREMENT," + " name TEXT)");
        db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_BOOK + "(_id INTEGER PRIMARY KEY AUTOINCREMENT," + " name TEXT)");
    }

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

    }
}
  1. 新建一个MyContentProvider类,继承ContentProvider类
public class MyContentProvider extends ContentProvider {

    private Context mContext;

    private MyDbHelper mMyDbHelper;

    // 设置ContentProvider的唯一标识
    public static final String AUTOHORITY = "com.returntolife.myprovider";
   

    public static final int TABLE_STUDENT_CODE = 1;
    public static final int TABLE_BOOKE_CODE = 2;

    // UriMatcher类使用:在ContentProvider 中注册URI
    private static final UriMatcher mMatcher;

    SQLiteDatabase db = null;

    //重要的一步,将uri和provider绑定
    static {
        mMatcher=new UriMatcher(UriMatcher.NO_MATCH);

        mMatcher.addURI(AUTOHORITY,MyDbHelper.TABLE_SUTEMT, TABLE_STUDENT_CODE);
        mMatcher.addURI(AUTOHORITY, MyDbHelper.TABLE_BOOK, TABLE_BOOKE_CODE);

    }

    @Override
    public boolean onCreate() {
        mContext=getContext();

        mMyDbHelper=new MyDbHelper(mContext);
        db=mMyDbHelper.getWritableDatabase();
        // 初始化两个表的数据(先清空两个表,再各加入一个记录)
        db.execSQL("delete from student");
        db.execSQL("insert into student values(1,'张三');");
        db.execSQL("insert into student values(2,'李四');");

        db.execSQL("delete from book");
        db.execSQL("insert into book values(1,'Android');");
        db.execSQL("insert into book values(2,'iOS');");

        return true;
    }

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

        String table=getTableName(uri);

        return db.query(table,null,null,null,null,null,null);
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        LogUtil.d("getType uri="+uri);

        return "vnd.android.cursor.dir/harvic.first";
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {

        String table=getTableName(uri);

        db.insert(table,null,values);
        // 当该URI的ContentProvider数据发生变化时,通知外界(即访问该ContentProvider数据的访问者)
        mContext.getContentResolver().notifyChange(uri, null);

        return uri;
    }

    @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_CODE,从而匹配ContentProvider中相应的表名
     */
    private String getTableName(Uri uri){
        String tableName = null;
        switch (mMatcher.match(uri)) {
            case TABLE_STUDENT_CODE:
                tableName = MyDbHelper.TABLE_SUTEMT;
                break;
            case TABLE_BOOKE_CODE:
                tableName = MyDbHelper.TABLE_BOOK;
                break;
            default:
                break;
        }
        return tableName;
    }
}
  1. 在AndroidManifest.xml文件注册这个ContentProvider,绑定一个Uri
     <provider
            android:authorities="com.returntolife.myprovider"
            android:name=".mycontentprovider.MyContentProvider"/>
  1. 自身内部访问直接通过ContentResolver就可以访问,不用添加什么权限
 private void getData() {
        ContentResolver contentResolver=getContentResolver();

        Uri uri=Uri.parse("content://com.returntolife.myprovider/student");

        Cursor cursor=contentResolver.query(uri,new String[]{"_id,name"},null,null,null);
        if(cursor!=null){
            while(cursor.moveToNext()){
                LogUtil.d("id="+cursor.getInt(0)+"--name="+cursor.getString(1));
                tvTest.append("id="+cursor.getInt(0)+"--name="+cursor.getString(1)+"\n");
            }
            cursor.close();
        }

        Uri uriBook=Uri.parse("content://com.returntolife.myprovider/book");

        Cursor cursorBook=contentResolver.query(uriBook,new String[]{"_id,name"},null,null,null);
        if(cursorBook!=null){
            while(cursorBook.moveToNext()){
                LogUtil.d("id="+cursorBook.getInt(0)+"--name="+cursorBook.getString(1));
            }
            cursorBook.close();
        }
    }
  1. 外部进程访问需要定义和添加权限
    5.1 提供数据的进程(进程1)中自定义一个访问权限,并设置到对应的provider中,如下
   <permission
        android:name="com.returntolife.jjconde.providertestservice.provider"
        android:protectionLevel="normal" />

 <provider
            android:name=".mycontentprovider.MyContentProvider"
            android:authorities="com.returntolife.myprovider"
            android:exported="true"
            android:permission="com.returntolife.myprovider.permission" />

5.2 需要访问数据的进程(进程2)中添加上面定义的访问权限,然后其余获取数据基本和应用自身获取数据一致

    <uses-permission android:name="com.returntolife.myprovider.permission"/>
        // 设置URI
        Uri uri=Uri.parse("content://com.returntolife.myprovider/student");

        // 插入表中数据
//        ContentValues values = new ContentValues();
//        values.put("_id", 10);
//        values.put("name", "Jordan");


        // 获取ContentResolver
        ContentResolver resolver =  getContentResolver();
        Cursor cursor = resolver.query(uri, new String[]{"_id","name"}, null, null, null);
        if(cursor!=null){
            while (cursor.moveToNext()){
                Log.d("MainActivity","id="+cursor.getInt(0)+"--name="+cursor.getString(1)+"\n");
            }
            cursor.close();
        }

总结

  1. contentProvider的难点主要是在理解uri的定义,以及如何将uri和provider绑定,访问方式其实不难
  2. contentProvider实现跨进程通信相对于AIDL的通信方式,会简洁方便很多

Demo地址

https://github.com/returntolife455/DemoList

参考文章

Android:关于ContentProvider的知识都在这里了!
ContentProvider数据库共享之——MIME类型与getType()

    原文作者:return_toLife
    原文地址: https://www.jianshu.com/p/d1ca6689cbd8
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞