相关概念的理解
内容提供者可以看成一个仓库,现在我们有各种功能的仓库,有的仓库存储短信信息,有的仓库存储着联系人。现在我想从短信仓库获取信息,就要通过内容解析者(可以看见仓库管理员,它管理着多个仓库)来操作短信仓库,从仓库获取信息。现在发现,我和仓库是单相连接的,也就是如果我不用内容解析者去操作仓库,我是不知道仓库内容的。假设我们要做一个功能,实时查询短信,是否有新的信息我是不知道,如果我定时去查询信息,是很消耗资源的。这是就需要一个人,替我看着仓库,当仓库资源变化时,要通知我,然后我在调用内容解析者,就可以实时查询短信信息了,而这个人就是内容观察者。
看完上边的,对某些概念还是不能理解,请向下看
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来区分表, 只是一种灵活的用法
完结:
知其然,知其所以然。内容提供者的使用原理,就是上边的,而具体使用并没有详细解释,相信当知道运行原理后,实现应用也不困难。