Android五大存储方式之四——ContentProvider

ContentProvider

ContentProvider是Android四大组件之一,用于保存和检索数剧,是Android系统中不同应用之间共享数据的接口。在Android系统中,应用程序之间是相互独立的,分别运行在自己的进程中,相互之间没有数据交换。若应用程序之间需要共享数据,就需要用到ContentProvider。

ContentProvider以URI的形式对外提供数据,其他应用则使用ContentResolver、并根据ContentProvider提供的URI来操作ContentProvider暴露的数据。

当应用需要实时监听ContentProvider共享的数据是否发生变化,可以使用ContentObserver来实现。

自定义ContentProvider

一般我们写程序时是利用其它程序的ContentProvider来读取数据,而很少要自己自定义一个ContentProvider,所以这里就只简单介绍一下ContentProvider的创建方法。

在创建一个ContentProvider时,首先需要定义一个类继承ContentProvider类,需要重写它的onCreate()、delete()、getType()、query()、update()这几个抽象方法。

public class PersonDBProvider extends ContentProvider{
/*创建时调用,适合数据的初始化*/
    public boolean onCreate() {
        return false;
    }
/*查询数据操作*/
    public Cursor query(Uri uri,String[] projection,String selection,String[] selectionArgs,String sortOrder) {
        return null;
    }
/*用来获取当前Uri路径指定数据的MIME类型,如.txt和.jpg,如果指定数据的类型属于集合型(多条数据),
getType()方法返回的字符串应该以“vnd.android.cursor.dir/”开头。如果属于非集合型(单条数据)
则返回的字符串以“vnd.android.cursor.item/”开头*/
    public String getType(Uri uri) {
        return null;
    }
/*添加数据*/
    public Uri insert(Uri uri,ContentValues values) {
        return null;
    }
/*删除数据*/
    public int delete(Uri uri,String selection,String[] selectionArgs) {
        return 0;
    }
/*更新数据*/
    public int update(Uri uri,ContentValues values,String selection,String[] selectionArgs) {
        return 0;
    }
}

注册ContentProvider

ContentProvider是Android的四大组件之一,因此需要和Activity一样在清单文件中注册。具体代码如下:

<provider
//acdroid:name代表继承于ContentProvider类的全路径名称
    acdroid:name="cn.csdn.db.PersonDBProvider"
//android:authorities代表了访问本provider的路径,注意这里的路径必须要唯一
    android:authorities="an.csdn.db.personprovider" >
</provider>

如何让暴露的数据更安全
当使用provider暴露敏感数据时,为了数据的安全,在注册ContentProvider时,还可以为其指定一系列权限。

  • android:permission属性:如果在注册provider时使用了该属性,那么其他程序在访问ContentProvider时必须加上该权限,否则会报异常。例如:PersonDBProvider注册了android:permission="moblie.permission.PROVIDER"那么在其他应用使用该provider时需要加上权限<uses-permission android:name="moblie.permission.PROVIDER"/>
  • android:readPermission属性:如果在注册provider时使用了该属性,那么其他应用通过ContentProvider的query()方法查询数据时,必须加上该权限。
  • android:writePermission属性:如果在注册provider时使用了该属性,那么其他应用通过ContentProvider的增删改方法查询数据时,必须加上该权限。

Uri简介

Uri是一个对象,用Uri.prase(String str)方法可以将字符串转换为Uri对象。
Uri是由schema、authorities、path三部分组成。
Uri组成结构图
schema部分content://是一个标准的前缀,不会被修改。authorities部分en.itcast.db.personprovider是在清单文件中指定的android:authorities属性值,该值必须唯一,它表示了当前的ContentProvider。path路径部分/person代表资源(或者数据),当访问者需要操作不同数据时,这个部分是动态改变的,表示具体操作于哪个条目。ID是代表具体到哪个条目下的哪条记录。

Android提供了一个辅助工具类UriMatcher用于匹配Uri。下面举一个具体例子解释这个UriMatcher类以及ContentProvider和提供数据的应用程序的数据库之间的关系。

public class PersonDBProvider extends ContentProvider{
/*定义一个uri的匹配器,用于匹配uri,如果路径不满足条件,返回-1*/
private static UriMatcher matcher=new UriMatcher(UriMatcher.NO_MATCH);
private static final int INSERT=1;   //匹配uri成功时的返回码
private static final int DELETE=2;
private static final int UPDATE=3;
private static final int QUERY=4;
private static final int QUERYONE=5;
//数据库操作类的对象
private PersonSQLiteOpenHelper helper;
static{
/*添加一组匹配规则,addURI的第一个参数是Uri的authorities部分,第二个参数是Uri的path部分*/
matcher.addURI("an.csdn.db.personprovider","insert",INSERT);
matcher.addURI("an.csdn.db.personprovider","delete",DELETE);
matcher.addURI("an.csdn.db.personprovider","update",UPDATE);
matcher.addURI("an.csdn.db.personprovider","query",QUERY);
//这里的“#”为通配符凡是符合“query/”皆返回QUREYONE返回码
matcher.addURI("an.csdn.db.personprovider","query/#",QUERYONE);
}
//创建时调用,数据初始化
    public boolean onCreate() {
        helper=new PersonSQLiteOpenHelper(getContext());
        return false;
    }
    //查询数据操作
    public Cursor query(Uri uri,String[] projection,String selection,String[] selectionArgs,String sortOrder) {
        if(matcher.match(uri)==QUERY){
        //匹配成功,调用数据库查询数据,返回结果集
        SQLiteDatabase db=helper.getReadableDatabase();
        Cursor cursor=db.query("person",projection,selection,selectionArgs,null,null,sortOrder);
        return cursor;
        }else if(matcher.match(uri)==QUERYONE){
        //匹配成功,返回一条数据
        long id=ContentUris.parseID(uri);//从路径中获取ID部分
        SQLiteDatabase db=helper.getReadableDatabase();
        Cursor cursor=db.query("person",projection,"id=?",new String[]{id+""},null,null,sortOrder);
        return cursor;
        }else{
        throw new IllegalArgumentException("路径不匹配!");}
    }
    //获取当前Uri的数据类型
    public String getType(Uri uri) {
        if(matcher.match(uri)==QUERY){ 
        return "vnd.android.cursor.dir/person";
        }else if(matcher.match(uri)==QUERYONE) {
        return "vnd.android.cursor.item/person";
        }
        return null;
    }
    //添加数据
    public Uri insert(Uri uri,ContentValues values) {
        if(matcher.match(uri)==INSERT){ 
        SQLiteDatabase db=helper.getReadableDatabase();
        db.insert("person",null,values);
        }else {
        throw new IllegalArgumentException("路径不匹配!");}
        return null;
    }
    //删除数据
    public int delete(Uri uri,String selection,String[] selectionArgs) {
        if(matcher.match(uri)==DELETE){ 
        SQLiteDatabase db=helper.getReadableDatabase();
        db.delete("person",selection,selectionArgs);
        }else {
        throw new IllegalArgumentException("路径不匹配!");}
        return 0;
    }
    //更新数据
    public int update(Uri uri,ContentValues values,String selection,String[] selectionArgs) {
        if(matcher.match(uri)==UPDATE){ 
        SQLiteDatabase db=helper.getReadableDatabase();
        db.update("person",values,selection,selectionArgs);
        }else {
        throw new IllegalArgumentException("路径不匹配!");}
        return 0;
    }
}

ContentResolver

在Android系统中,ContentResolver充当着一个中介的角色。
应用程序通过ContentResolver根据相应的Uri来操作ContentProvider暴露出的数据。

注意:这里的Uri只能提供查询操作!
具体代码如下所示:

//获取相应的Uri
Uri uri=Uri.parse("content://cn.itcast.db.personprovider/person");
//获取ContentResolver对象
ContentResolver resolver=context.getContentResolver();
//通过ContentResolver对象查询数据
Cursor cursor=resolver.query(uri,new String[]{"address","date","type","body"},null,null,null);
while(cursor.moveToNext()){
    String address=cursor.getString(0);
    long date=cursor.getLong(1);
    int type=cursor.getInt(2);
    String body=cursor.getString(3);
}
cursor.close();

ContentObserver

ContentObserver 是用来观察Uri所代表的数据。当ContentObserver 观察到指定Uri代表的数据发生变化时,就会触发ContentObserver的onChange()方法。此时在onChange()方法里使用ContentResolver可以查询到变化的数据。

此时的ContentProvider中的delete()、insert()、update()方法中要调用ContentResolver的notifyChange()方法。代码如下:
//添加数据
public Uri insert(Uri uri,ContentValues values) {
        if(matcher.match(uri)==INSERT){ 
        SQLiteDatabase db=helper.getReadableDatabase();
        db.insert("person",null,values);
/*PersonDao是用于往数据库中添加数据的类,PersonDao.messageuri是当前发生改变的记录的Uri,null是ContentObserver参数,这个参数表示制定具体的ContentObserver接收消息,如果不指定则传入null*/
        getContext().getContentResolver().notifyChange(PersonDao.messageuri,null);

        }else {
        throw new IllegalArgumentException("路径不匹配!");}
        return null;
    }

实现在应用中ContentObserver,并监听数据变化的功能的代码如下:

public class MainActivity extends Activity{
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //获取ContentResolver对象
    ContentResolver resolver=getContentResolver();
    Uri uri=Uri.parse("content://aaa.bbb.ccc");
    //注册ContentObserver
    resolver.registerContentObserver(uri,true,new MyObserver(new Handler());
    }
    //自定义的ContentObserver
    private class MyObserver extends ContentObserver{
    public MyObserver(Handler handler){
        super(handler);
    }
    //当MyObserver观察到数据库的内容变化了,调用这个方法
    public void onChange(boolean selfChange){
        super.onChange(selfChange);
        Uri uri=Uri.parse("content://aaa.bbb.ccc");
        ContentResolver resolver=getContentResolver();
        //通过ContentResolver对象查询出变化的数据
        Cursor cursor=resolver.query(uri,new String[]{"address","date","type","body"},null,null,null);
        cursor.moveToFirst();
        String address=cursor.getString(0);
        String body=cursor.getString(3);
        //用log打印更新的信息
        Log.v("MyObserver","body");
        cursor.close();
    }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值