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三部分组成。
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();
}
}
}