6.1理论概述
ContentProvider是四大应用组件之一,用于保存和检索数据,是Android系统中不同应用程序之间共享数据的接口。在Android系统中,应用程序之间是相互独立的,分别运行在自己的进程中,相互之间没有数据交换。若应用程序之间需要共享数据就需要用到ContentProvider。
ContentProvider是不同应用程序之间进行数据交换的标志API,它以Uri的形式对外提供数据,允许其他应用操作本应用的数据。其他应用则使用ContentResolver,并根据ContentProvider提供的Uri操作指定数据。
• 当前应用使用ContentProvider将数据库表数据操作暴露给其它应用访问
• 其它应用需要使用ContentResolver来调用ContentProvider的方法
• 它们之间的调用是通过Uri来进行交流的
image image image image image6.2 创建内容提供者
首先需要定义一个类继承android:content包下的ConteentProvider类,它是一个抽象类,在使用该类时需要重写它的onCreate()、delete()、insert()、query()、update()这几个抽象方法。
image6.2.1 ContentProvider主要方法
方法名称 | 方法描述 |
---|---|
public boolean onCreate() | 创建ContentProvider时调用 |
public int delete(Uri uri,String selection, String[]selectionArgs) | 根据传入的Uri删除指定条件下的数据 |
public Uri insert(Uri uri,ContentValues values) | 根据传入的Uri插入数据 |
public Cursor query(Uri uri,String[]projection,String selection,String[]selectionArgs,String sortOrder) | 得根据传入的Uri查询指定条件下的数据 |
public int update(Uri uri,ContentValues values,String selection,String[]selectionArgs) | 根据传入的Uri更新指定条件下数据 |
public String getType(Uri uri) | 用于返回指定Uri代表数据的MIME类型 |
其中getType()方法时用来获取当前Uri路径指定数据的类型。这个类型表示当前Uri指定数据的MIME类型,例如,在Windows系统下.txt的文件和.jpg的文件就是两种不同的类型。
如果指定数据的类型属于集合型(多条数据),getType()方法返回的字符串应该以"vnd.android.cursor.dir/"开头。如果属于非集合(单条数据)型则返回的字符串以"vnd.android.cursor.item/"开头。
6.2.2 创建内容提供者
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;
}
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是Android四大组件之一,因此需要和Activity一样在清单文件中注册。具体代码如下所示:
<provider
android:name="cn.itcast.db.PersonDBProvider"
android:authorities="cn.itcast.db.personprovider">
</provider>
android:name代表继承于ContentProvider类的全路径名称,android:authorities代表访问本provider的路径,路径必须唯一
让暴露的数据更安全
当使用provider暴露敏感数据时,为了数据安全,在注册ContentProvider时,还可以指定一些列的权限,具体如下:
-
android:permission属性:如果注册provider时使用了该属性,那么其他应用程序在访问ContentProvider时必须加上该权限,否则会报异常。例如PersonDBProvider注册了android:permission="mobile.permission.PROVIDER"那么在其他应用使用该provider时需要加上权限<uses-permission android:name="mobile.permission.PROVIDER"/>.
-
android:readPermission属性:如果在注册provider时使用了该属性那么其他应用程序通过ContentProvider的query()方法查询数据的时候,必须加上该权限
-
android:writePermission属性:使用该属性,那么其他应用程序通过ContentProvider的增删改这几个方法操作数据时,必须加上该权限
-
如果注册provider时,为其指定了自定义的权限(即系统中不存在的权限)。为了让自定义权限生效,首先需要单击清单文件中的permission标签页的Add按钮,填写Permission标签信息填写并保存
6.3 Uri简介
Uri代表了数据的操作方法,Uri是由scheme、authorites、path三部分组成
例如:
content://cn.itcast.db.personprovider/person
scheme部分content"//是一个标准前缀,表明这个数据被内容提供者所控制,它不会被修改。authorities部分cn.itcast.db.personprovider是在清单文件中指定的android:authorities属性值,该值必须唯一,它表示了当前内容的提供者。path部分/person代表资源(或者数据),当初访问者需要操作不同数据时,这个部分是动态变化的。
uri.parse(String str)方法是将字符串转化成Uri对象的。为了解析Uri对象Android提供了一个辅助工具类UriMatcher用于匹配Uri对象。
6.3.1 UriMatcher的常用方法
方法名称 | 方法描述 |
---|---|
public UriMatcher(int code) | 创建UriMatcher对象时调用,参数通常使用UriMatcher.NO_MATCH,表示路径不满足条件返回-1 |
public void addURI(String authority,String path,int code) | 添加一组匹配规则,authority即Uri的authorities部分,path即Uri 的path部分 |
public int match(Uri uri) | 匹配Uri与addURI方法相对应,匹配成功则返回addURI方法中传入的参数code的值。 |
6.4 访问内容提供者
ContentResolver充当着一个中介的角色。应用程序通过ContentProvider暴露自己的数据,通过ContentResolver对应应用程序暴露的数据进行操作。
image由于使用ContentProvider暴露数据时提供了相应操作的Uri,因此在访问现有的ContentProvider时要指定相应的Uri,然后通过ContentResolver对象来实现数据的操作。具体代码示例如下:
//获取相应的操作的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();
使用ContentResolver对象的query()方法实现对数据的查询功能,需要注意的是,这里的Uri只能提供查询操作,如果使用查询操作的Uri进行更新操作会抛异常。
6.5 内容观察者的使用
如果应用程序需要实时监听ContentProvider共享的数据是否发生改变,可以使用Android系统提供的内容容观察者(ContentObserver)来实现。
内容观察者是用来观察指定Uri所代表的数据。当ContentObserve观察到指定Uri代表的数据发生变化时,就会触发ContentObserve的onChange()方法。此时在onChange()方法里使用ContentResolver可以查询到变化的数据。
6.5.1 ContentObserve常用方法
方法名称 | 方法描述 |
---|---|
public void ContentObserver(Handler handler) | ContentObserve的派生类都需要执行该构造方法。参数可以是主线程Handler(可以更新UI),也可以是任何handler对象 |
public void onChange(boolean selfChange) | 当观察到的Uri代表的数据发生变化时,会触发该方法。 |
使用ContentProvider时,通过delete()、insert()、update()这几个方法让数据发生变化。乳沟要使用ContentObserve观察者数据变化,就必须在ContentProvider中的delete()、insert()、update()方法中调用ContentResolver的notifyChange()方法。具体如下所示:
//添加数据
public Uri insert(Uri uri,ContentValues values)
{
if(matcher.mathc(uri)==INSERT)//匹配成功
{
//匹配成功
SQLiteDatabase db=helper.getWritableDatabase();
db.insert("person",null,values);
getContext.getContentResolver().notifyChange(PersonDao.messageuri,null);
}
else//匹配不成功
{
throw new IllegargumentException("路径不匹配,不能执行插入");
}
return null;
}
notiryChange(Uri uri,:ContentObserver eob)方法。参数ContentObserve表示数据发生变化时指定具体的观察者接收消息,如果不指定具体的观察者则传入null即可
6.5.2 实现应用中注册观察者,并监听数据变化
public class MainActivity extends Activity
{
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取ContentObserve对象
ContentObserve resolver=getContentResolver();
Uri uri=Uri.parse("content://aaa.bbb.ccc");
//注册内容观察者
resolver.registerContentObserver(uri,true,new MyBoserver(new Handler()));
}
//自定义的内容观察者
private class MyObserver extendss ContentObserver
{
public MyObserver(Handler hander)
{
super(handler);
}
//当内容观察者观察到数据库的内容变化了,调用这个方法
public void onChange(boolean selfChange)
{
super.onChange(selfChange);
Toast。。。。
Uri uri=Uri .parse("content://aaa.bbb.ccc");
//获取ContentObserve对象
ContentObserve resolver=getContentResolver();
//通过ContentObserve对象查询出变化的数据
Cursor cursor=reolver.query(uri,new String[]{"address","data","type","body"},null,null,null);
cursor.moveToFirst();
String address=cursor.getString(0);
String body=cursor.getString(3);
cursor.close();
}
}
}
网友评论