android contentprovider简单讲解

文章的开头奉送上代码方便大家对照学习。

1 ContentProvider简介

ContentProvider是android4大组件之一,它的出现是为了在二个应用之间共享数据。

在Android中,对数据的保护是很严密的,除了放在SD卡中的数据,一个应用所持有的数据库、文件、等等内容,都是不允许其他直接访问的。但有时候沟通是必要的,不仅对第三方很重要,对应用自己也很重要。比如,一个联系人管理的应用。如果不允许第三方的应用对其联系人数据库进行增删该查,整个应用就失去了可扩展力,必将被其他应用抛弃,然后另立门户,自个玩自个的去了。Andorid当然不会真的把每个应用都做成一座孤岛,它为所有应用都准备了一扇窗,这就是Content Provider。

Content Provider屏蔽了内部数据的存储细节,向外提供了上述统一的接口模型。这样大大简化了我们代码的书写。

在使用ContentProvider时我们需要用到以下工具,这里先做简单介绍,下面会仔细讲解:

功能
Uri外部应用唯一确定要访问的ContentProvider
ContentResolver外部应用用来获取ContentProvider对象,进行增删改查操作
UriMatcher
ContentUris
ContentProvider给外部应用提供接口,用统一的方式获取共享数据

2 ContentProvider相关类的介绍

这些类大家看不懂也不要紧,大体看一下心里有个印象就行。我们在contentProvider实例讲解用到的时候直接到这里面查阅就行。

2.1 Uri介绍

外部应用通过uri来唯一标识一个ContentProvider。
举个例子:
这里写图片描述

由图中可以看出uri分3部分:

1.scheme:content://
固定写法,表示这个uri指定一个contentprovider(即我们要访问的是一个contentprovider)。

2.主机名(或叫Authority)用于唯一标识一个contentyprovider。注意这个主机名必须和menifest中contentProvider的authorities属性一样,不然找不到我们需要的contentyprovider。在本例子中主机名是:com.ljq.provider.personprovider

3.路径用来表示我们要操作的数据,路径的构建应根据业务而定。例如:

/person/10:要操作person表中id为10的记录
person/10/name:要操作person表中id为10的记录的name字段, 
/person:要操作person表中的所有记录
/xxx:要操作xxx表中的记录

如果你想创建自己的contentprovider,最好把自定义的URI设置为常量,这样简化别人的调用,并且以后如果更新URI也很容易。android定义了CONTENT_URI常量用于URI,比如:
public static final Uri CONTENT_URI =
Uri.parse(“content://contacts/people”);
可能大家看到这里还是不知道uri怎么用,我学习的时候也有这个疑问,不要着急,uri要结合UriMatcher和ContentUris一块使用的,下面会讲到。

2.2 ContentResolver

在外部应用的activity中通过getContentResolver()方法来获取对象,然后进行增删改查操作,步骤如下:
1.获取ContentResolver。

getContentResolver()

2.利用ContentResolver增删改查:

增:getContentResolver().insert(Uri url, ContentValues values);
删:getContentResolver().delete(Uri url, String where,  String[] selectionArgs)
改:getContentResolver().update(Uri uri,  ContentValues values,  String where, String[] selectionArgs)
查:getContentResolver().query(Uri uri,  String[] projection,  String selection,  String[] selectionArgs,  String sortOrder);

在这4个方法中,每一个方法都需要一个uri,因为这个uri唯一标识一个contentyprovider(内部是怎么实现的我们下面会讲到,这里不做介绍)。其他的参数就和sqlite操作的参数大同小异,这里不做介绍。

2.3 UriMatcher

UriMatcher用来匹配uri的作用。UriMatcher中只有3个方法(包括构造方法):

1.UriMatcher(int code)方法
构造方法,固定写法:new UriMatcher(UriMatcher.NO_MATCH)

2.addURI(String authority, String path, int code)方法
添加匹配规则

参数介绍
authority主机名
path路径
code匹配码,当匹配成功后,就会返回这个码

这里对path讲一下,因为它比较灵活,”*”表于匹配所有的字符串,”#” 表示匹配所有数字,如:

    uri的路径是: “/people”
    sURIMatcher.addURI("contacts", "people", PEOPLE);
    uri的路径是: “/people/10”
    sURIMatcher.addURI("contacts", "people/#", PEOPLE_ID);
    uri的路径是: “/people/10/phones”
    sURIMatcher.addURI("contacts", "people/#/phones", PEOPLE_PHONES);
    uri的路径是: “/people/10/phones/3”
    sURIMatcher.addURI("contacts", "people/#/phones/#", PEOPLE_PHONES_ID);

3.match(Uri uri)方法
根据addURI()方法添加的匹配规则来匹配,如果匹配到了就会返回响应的匹配码。如:

 int matchId= sURIMatcher.match(uri);
 switch(matchId){
    case 1://表示访问的student表
    break;
    case 2://表示访问的是teacher表
    break;
}

2.4 ContentProvider

终于讲到我们的重点了,使用步骤如下:
1.继承ContentProvider重写它的方法:

onCreate()
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
getType(Uri uri)
insert(Uri uri, ContentValues values)
delete(Uri uri, String selection, String[] selectionArgs)
 update(Uri uri, ContentValues values, String selection, String[] selectionArgs)

在ContentProvider中,我们要给UriMatcher添加匹配规则,做匹配操作。
2.在menifenst中注册:

<provider
android:name=".contentprovider.MyContentProvider"
android:authorities="com.czh.mycontentprovider.study" />

注意android:authorities是主机名,唯一标识这个ContentProvider,外部应用也是通过这个来查找。

2.5 补充ContentUris

ContentUris是一个辅助工具类,里面全是静态方法,我们看一下:

方法介绍
appendId(Uri.Builder builder, long id)在路径后添加id
parseId(Uri contentUri)将路径的最后一个参数转化成long数据,一般用来获取id
withAppendedId(Uri contentUri, long id)在uri后添加id

3 ContentProvider实例

呼!终于讲到实例了。接下来我们自定义ContentProvider来访问数据库。

第一步重写contentProvider:

public class MyContentProvider extends ContentProvider {
    private final static int MyStudyTableFlag = 1;//查询表的标记,建议大家都弄成常量
    private final static int MyStudyTableNumFlag = 2;//查询表的标记,建议大家都弄成常量
    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    static{//静态语句块,当第一次加载的时候调用
        //匹配查询表
        sURIMatcher.addURI(Constant.Authorities, Constant.TableName, MyStudyTableFlag);
        //匹配查询表中的特定记录
        sURIMatcher.addURI(Constant.Authorities, Constant.TableName+"/#", MyStudyTableNumFlag);
    }
    @Override
    public boolean onCreate() {
        Log.d(Constant.TAG,"--MyContentProvider--onCreate--");
        return false;
    }

    @Nullable
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        Log.d(Constant.TAG,"--MyContentProvider--query--");
        int matchId= sURIMatcher.match(uri);
        switch(matchId) {
            case MyStudyTableFlag://如果查询的是表
                SQLiteDatabase db1 = SqlManager.getInstance(getContext()).getWritableDatabase();
//                String selectSql1 = "select * from " + Constant.TableName;
                Cursor cursor1 = db1.query(true, Constant.TableName, projection,
                        selection, selectionArgs, null, null, null, null);
                return cursor1;
            case MyStudyTableNumFlag://如果查询的是表中的确定id的记录
                long id = ContentUris.parseId(uri);//ContentUris的使用。
                Log.d(Constant.TAG, "ContentUris:" + id);
                SQLiteDatabase db2 = SqlManager.getInstance(getContext()).getWritableDatabase();
                //查找id相匹配的数据。
                String selectSql2 = "select * from " + Constant.TableName + " where _id = ?";
                String[] args = {"" + id};
                Cursor cursor2 = db2.rawQuery(selectSql2, args);
                return cursor2;
        }
        return null;
    }

    @Nullable
    @Override
    public String getType(Uri uri) {
        Log.d(Constant.TAG,"--MyContentProvider--getType--");
        return null;
    }

    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        SQLiteDatabase db = SqlManager.getInstance(getContext()).getWritableDatabase();
        db.insert(Constant.TableName,null,values);
        Log.d(Constant.TAG,"--MyContentProvider--insert--");
        return uri;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        Log.d(Constant.TAG,"--MyContentProvider--delete--");
        int matchId= sURIMatcher.match(uri);
        switch(matchId) {
            case MyStudyTableFlag://
                SQLiteDatabase db1 = SqlManager.getInstance(getContext()).getWritableDatabase();
//                String selectSql1 = "select * from " + Constant.TableName;
                db1.delete(Constant.TableName, selection, selectionArgs);
                break;
            case MyStudyTableNumFlag:
                long id = ContentUris.parseId(uri);//ContentUris的使用。
                Log.d(Constant.TAG, "ContentUris:" + id);
                SQLiteDatabase db2 = SqlManager.getInstance(getContext()).getWritableDatabase();
                String[] args = {"" + id};
                db2.delete(Constant.TableName, "_id = ?", args);
            break;
        }
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        Log.d(Constant.TAG,"--MyContentProvider--update--");
        int matchId= sURIMatcher.match(uri);
        switch(matchId) {
            case MyStudyTableFlag:
                SQLiteDatabase db1 = SqlManager.getInstance(getContext()).getWritableDatabase();
                db1.update(Constant.TableName, values, selection, selectionArgs);
                break;
            case MyStudyTableNumFlag:
                long id = ContentUris.parseId(uri);//ContentUris的使用。
                Log.d(Constant.TAG, "ContentUris:" + id);
                SQLiteDatabase db2 = SqlManager.getInstance(getContext()).getWritableDatabase();
                String[] args = {"" + id};
                db2.update(Constant.TableName, values,"_id = ?", args);
                break;
        }
        return 0;
    }
}

第二步注册contentProvider:

  <provider
            android:name=".contentprovider.MyContentProvider"
  android:authorities="com.czh.mycontentprovider.study" />

第三步外部应用增删改查
查找数据的代码:

String uriStr = "content://com.czh.mycontentprovider.study/"+Constant.TableName+"/4";
String[] projection = {Constant._ID,Constant.NAME,Constant.AGE};
String selection = "_id = ?";
String[] selectionArgs = {"2"};
Cursor cursor =  getContentResolver().query(Uri.parse(uriStr),projection,selection,selectionArgs,null,null);
CursorAdapter ca = new SqlAdapter(MainActivity.this, cursor);
listView.setAdapter(ca);

插入数据的代码:

   String uriStr = "content://com.czh.mycontentprovider.study";
        ContentValues cv = new ContentValues();
        cv.put("name","yyd插入测试");
        cv.put("age",100);
        getContentResolver().insert(Uri.parse(uriStr), cv);

删除数据的代码:

 String uriStr = "content://com.czh.mycontentprovider.study/"+Constant.TableName+"/10";
        // Uri url,  String where,  String[] selectionArgs
        getContentResolver().delete(Uri.parse(uriStr),null,null);

更新数据的代码:

 String uriStr = "content://com.czh.mycontentprovider.study/"+Constant.TableName+"/10";
getContentResolver().delete(Uri.parse(uriStr),null,null);

4 contentprovider使用小结

外部应用操作步骤:

第一步获取ContentResolver

ContentResolver cr = getContentResolver();

第二步:

利用ContentResolver,结合uri进行增删改查。

主应用使用步骤:

第一步:继承contentprovider,重写它的方法
第二步:在注册contentprovider。

监听ContentProvider中数据的变化

步骤:
1.在ContentProvider类的insert\update\delete方法添加改变通知:

this.getContext().getContentResolver().notifyChange(URI,null);
参数介绍
uri表示监听的URI
null表示发送消息给任何人;

2.在外部应用设置监听:

getContentResolver().registerContentObserver(Uri.parse("content://com.czh.mycontentprovider.study"),true,new PersonObserver(new Handler()));

 private class PersonObserver extends ContentObserver{//监听
        public PersonObserver(Handler handler) {
            super(handler);
        }
        //当ContentProvier数据发生改变,则触发该函数
        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
            Log.i("myapp", "数据改变");
        }
    }

挺简单的,这里不过多讨论。

5 ContentProvider深入理解

讲到最后了,我们来讨论几个问题:

1. 在应用程序A里面怎么跨进程拿到ContentProvider的对象呢?
2. ContentProvider实例对象是保存在哪里呢?
3. ContentProvider的方法实现要注意线程安全吗?

这块还没研究透彻,后面补充。

6 参考文档

【1】Android开发手册
【2】内容提供程序
【3】ContentProvider浅析—写点你平时没注意到的

7 结尾

文章的结尾奉送上代码方便大家对照学习。
好了就讲到这里吧。希望对大家有所帮助!如果觉的我写的不错,就点个赞吧!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序编织梦想

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值