ContentProvider(内容提供者)入门---android四大组件之一

相关概念的理解

       内容提供者可以看成一个仓库,现在我们有各种功能的仓库,有的仓库存储短信信息,有的仓库存储着联系人。现在我想从短信仓库获取信息,就要通过内容解析者(可以看见仓库管理员,它管理着多个仓库)来操作短信仓库,从仓库获取信息。现在发现,我和仓库是单相连接的,也就是如果我不用内容解析者去操作仓库,我是不知道仓库内容的。假设我们要做一个功能,实时查询短信,是否有新的信息我是不知道,如果我定时去查询信息,是很消耗资源的。这是就需要一个人,替我看着仓库,当仓库资源变化时,要通知我,然后我在调用内容解析者,就可以实时查询短信信息了,而这个人就是内容观察者

        看完上边的,对某些概念还是不能理解,请向下看

1、内容解析者(内容管理者)

内容解析者 ContentResolver 也可以说内容管理者,俗称仓库管理员,它管理多个仓库。

我们以查询短信信息为例:

 //获取ContentResolver对象
    ContentResolver resolver = getContentResolver();
 //上面说,内容管理者,管理了多个仓库,现在我要获取短信仓库的内容。如何指定仓库,就要通过uri。
 //uri格式:<prefix>://<authority>/<data_type>/<id>
    Uri uri = Uri.parse("content://sms/");
 //现在内容管理者有了,rui也有了,就可以操作了
 //通过ContentResolver对象查询系统短信
 //参数分别为:uri(仓库标识地址),要查询的字段,查询where字句,查询条件属性值,结果排序规则
    Cursor cursor = resolver.query(uri,new String[]{"_id","address","type","body","date"},null,null,null);

好了,简单吧。内容解析者的查询就ok了, 

现在查询到的短信,就存储在cursor里面了,现在把它打印一下。因为涉及到读取短信,我们给它一个权限

//1,将cursor中的信息提示出来
 if(cursor != null && cursor.getCount() > 0){
        while (cursor.moveToNext()){
            int _id = cursor.getInt(0);
            String address = cursor.getString(1);
            int type = cursor.getInt(2);
            String body = cursor.getString(3);
            long date = cursor.getLong(4);
            
            Log.i("输出",""+_id+address+type+body+date);
        }
        cursor.close();
//2、	在清单例表 允许读短信的权限
<uses-permission android:name="android.permission.READ_SMS"/>

 

2、内容观察者

我们来把它完善一下,让它实时查询短信信息。这时要需要一个内容观察者,当有新信息,我们在执行上边的操作,这样就可以实时查询了。

1、下边创建一个内容观察者:

//自定义的内容观察者 1、继承类ContentObserver
private class MyObserver extends ContentObserver{
    //构造方法 2,写死的
    public MyObserver(Handler handler) {
        super(handler);
    }
    //当内容观察者观察到数据库的内容发生变化时调用这个方法
    //当内容提供者的数据变化时,会默认调用这个方法。在这个方法内调用内容解析者要ok了
    public void onChange(boolean selfChange){
        super.onChange(selfChange);
        
        //这里调用内容解析者,查询短信中的数据
    }
}

2、注册内容观察者

现在有没有好奇,我即然是内容观察者,我观察谁啊,得告诉我啊,所以我们就要注册一下,在这个过程中告诉它监听那个仓库,这样当仓库数据变化时就可以调用相关方法了

//获取ContentResolver对象,内容观察者可以看成内容解析者的小弟,由内容解析者设定内容观察者监视那个仓库
//获取内容解析者
ContentResolver resolver = getContentResolver();
//指定短信仓库
Uri uri = Uri.parse("content://sms/");
//注册内容观察者,这样当uri代表的内容内容观察者数据变化时,就是调用对应的内容观察者
//false :表示精确匹配,即只匹配该Uri 。true :表示可以同时匹配其派生的Uri 
resolver.registerContentObserver(uri,true,new MyObserver(new Handler()));

好了,现在当有新的短信,MyObserver就是知道了,在其onChange方法就可以调用对应的方法处理了

 

3、内容提供者

内容提供者可以看成仓库,内容观察者就是看门的,内容解析者就是总的管理员。

现在想两个问题:

       1)如果给一个 内容提供者(仓库)指定一个url

       2)上面说,当内容提供者数据变化时,内容观察者就能触发onChange()方法。这个触发条件是怎么生成的

1、创建一个内容提供者

      选择 new -> other -> Content Provider。输入Class Name(名称)和url Authorities(唯一标识,通常使用包名,注意不要重复)

      下面是自动生成的:

 

//1、AndroidManifest.xml清单内容
            <provider
            android:name=".PersonProvider"
            android:authorities="com.example.userregist"
            android:enabled="true"
            android:exported="true"></provider>
   //android:authorities="com.example.userregist" 可以看成一个拦截器,当内容解析者指定的url一样是就会被拦截。在执行name对应的方法
//2、生成的java类,类名为PersonProvider
    public class MyContentProvider extends ContentProvider {
    public MyContentProvider() {
    }
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
    }
    //....由于篇幅原因,其它方法不列举

    }

下面看一下内容解析者执行  resolver.query(uri,new String[]{"_id","address","type","body","date"},null,null,null); 的执行流程。

先是通过uri在清单列表被拦截(android:authorities="com.example.userregist"),在通过name找到对应的类,在执行对应类的query()方法。也时就可以知道,具体查询的语句还是内容提供者编写的。

现在在思考下面问题:

       1)当内容提供者数据变化时,内容观察者就能触发onChange()方法。这个触发条件是怎么生成的(上面解决了uri指定仓库的问题)

        2)上面说的仓库一般是抽象的说法,很多时候数据是保存在数据库中的,而数据库中有多张表,这里如果指定要操作的表

问题一:当内容提供者数据变化时,内容观察者就能触发onChange()方法。这个触发条件是怎么生成的

@Override
public Uri insert(Uri uri, ContentValues values) {
        Log.e("内容提供者","正在插入");
       //dbhelper是一个继承SQLiteOpenHelper 类的实例,因为这里只是子解思路,就先不写了
        SQLiteDatabase db = helper.getReadableDatabase();
            long rowId = db.insert("info",null,values);
            
            if(rowId > 0){
                Uri insertedUri = ContentUris.withAppendedId(uri,rowId);
                //提示数据库的内容变化了
                getContext().getContentResolver().notifyChange(insertedUri,null);
                return  insertedUri;
            }
            db.close();
            return uri;
    }

可以发现

               Uri insertedUri = ContentUris.withAppendedId(uri,rowId);
                //提示数据库的内容变化了
                getContext().getContentResolver().notifyChange(insertedUri,null);

这两句,就是触发条件,一执行notifyChange()方法,内容观察者就会知道。

为什么要加if(rowId>0),如果不成立,说明没有修改数据,也就没有必须通知观察者了

问题二:上面说的仓库一般是抽象的说法,很多时候数据是保存在数据库中的,而数据库中有多张表,这里如果指定要操作的表

    //定义一个uri路径的匹配器,如果路径匹配不成功返回-1
    private static UriMatcher mUriMatcher = new UriMatcher(-1);
    //匹配路径成功时的返回值
    private static final int SUCCESS = 1;
    //添加路径匹配规则
    static {
        mUriMatcher.addURI("com.example.userregist.PersonProvider","info",SUCCESS);
    }

这里定义了一个匹配规则,以后我们就可以让uri和匹配规则进行匹配

int code = mUriMatcher.match(uri);
        if(code == SUCCESS){
            SQLiteDatabase db = helper.getReadableDatabase();
            return db.query("info",strings,s,strings,null,null,s1);
        }else {
            throw new IllegalArgumentException("你操作的表不是info,");
        }

解析器解析的时候,发送uri格式为:

uri = Uri.parse("content://com.example.userregist/info");
Cursor cursor = resolver.query(uri,new String[]{"_id","name"},null,null,null);

这样就可以通过发送uri的格式来区分表。这里说可以通过uri来区分表, 只是一种灵活的用法

 

完结:

知其然,知其所以然。内容提供者的使用原理,就是上边的,而具体使用并没有详细解释,相信当知道运行原理后,实现应用也不困难。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值