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

mac2025-11-07  11

相关概念的理解

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

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

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)