- 简介
Content Provider主要用于在不同的应用程序之间(因而是完成IPC的一种)实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访数据的安全性。目前,使用ContentProvider是 Android 实现跨程序共享数据的标准方式,因为它提供了统一的数据访问方式。 - 使用:
一种是使用现有的ContentProvider来读取和操作相应程序中的数据(eg:读取电话簿,通讯录信息);另一种是创建自己的Contentprovider为其它应用程序提供数据访问的外部接口。 - 对于每一个应用程序来说,如果想要访问内容提供器中共享的数据,就一定要借助ContentResolve类,可以通过 Context中的 getContentResolver()方法获取到该类的实例。
- ContentResolver 中提供了一系列的方法用于对数据进行 CRUD 操作。类似于SQLite中数据操作。
- 自定义contentprovider
很简单,通过继承contentprovider抽象类,并实现6个必要的方法;
onCreate() //contentprovider创建时候调用,完成初始化工作,由系统回调并运行在UI线程中。
String getType(URI uri)//用于返回一个URI所对应的MIME类型;(这里不太明白)
除了上边几个方法,还有四个方法是用于CRUD的,他们完成与数据库之间的交互,运行在Binder线程池中(包括getType方法)。 - 既然说contentprovider能为外部提供数据访问的接口,那么应用程序是通过什么方式来访问数据,contentprovider又是如何确定究竟是访问的那些数据?
数据是以集合的形式存在于数据库中,contentprovider提供了Uri来区分外界要访问的具体数据集合;
eg:content://com.exam.demodata.utils.MyContentProvider/person/2
上面的URI可以由3部分组成:
scheme:规定为content://,默认
Authority:用于标识一个contentprovider,便于外部访问者能找到它。也就是在注册文件中authorities中的内容;
path:用于标识要操作的数据的具体路径;上面的URI中的path部分为/person/2 表示应用程序将访问person表中id为2的数据。
又如,操作的是整个表,path为/person
- UriMatcher类:
为了知道外界将要访问的数据集合,通常会在contentprovider中自定义URI和URI_CODE,将它们一一关联。而这步操作通常是由UriMatcher类完成。
通过UriMatcher的addURI()方法建立URI和URI_CODE之间的关联,我们就可以根据URI得到URI_CODE(match方法),从而找到将要操作的具体数据。
eg:当contentprovider要提供对两个表的操作,可以通过此方法来确定外界究竟要访问那个表。
private MyDbHelper dbHelper;
private static final String authority="com.exam.demodata.utils.MyContentProvider";
public static final Uri contentUri
=Uri.parse("content://"+authority+"/person");
private static UriMatcher uriMatcher=new UriMatcher(UriMatcher.NO_MATCH);
static{
Log.i("info", "static");
uriMatcher.addURI(authority, "person", 1);
uriMatcher.addURI(authority, "person/#", 2);
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
SQLiteDatabase database=dbHelper.getWritableDatabase();
int num=0;
switch (uriMatcher.match(uri)) {
case 1:
num = database.delete("person", selection, selectionArgs);
break;
case 2:
long id=ContentUris.parseId(uri);
if(selection==null){
selection="id="+id;
}else{
selection="id=+"+id+"and ("+selection+")";
}
num=database.delete("person", selection, selectionArgs);
break;
default:
break;
}
return num;
}
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
Log.i("info", "getType");
switch (uriMatcher.match(uri)) {
case 1:
// 操作的数据类型为集合类型,返回值以vnd.android.cursor.dir/开头
// eg:得到person表的所有数据
return "vnd.android.cursor.dir/person";
case 2:
// 操作的数据类型为非集合类型,返回值以vnd.android.cursor.item/开头
// 得到person表下面,每一条数据
return "vnd.android.cursor.item/person";
}
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
SQLiteDatabase db=dbHelper.getWritableDatabase();
long id=db.insert("person", null, values);//id?
if(id>-1){
//构造新差入行的Uri
Uri uri2=ContentUris.withAppendedId(contentUri, id);
//通知所有的观察者,数据集已经发生了改变
getContext().getContentResolver().notifyChange(uri2, null);
return uri2;
}
return null;
}
@Override
public boolean onCreate() {
Log.i("info", "oncreate");
dbHelper=new MyDbHelper(getContext(), "mydb.db", null, 1);
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
// TODO Auto-generated method stub
SQLiteDatabase db=dbHelper.getReadableDatabase();
Cursor cursor = null;
switch (uriMatcher.match(uri)) {
case 1:
cursor=db.query("person",
null, //列的数组,null 代表所有的列
selection, selectionArgs,
null, //分组
null,//having
sortOrder);
break;
case 2:
long id=ContentUris.parseId(uri);
if(selection==null){
selection="id="+id;
}else{
selection="id=+"+id+"and ("+selection+")";
}
cursor=db.query("person",
null, //列的数组,null 代表所有的列
selection, selectionArgs,
null, //分组
null,//having
sortOrder);
break;
default:
break;
}
return cursor;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
SQLiteDatabase db=dbHelper.getWritableDatabase();
int num=0;//收影响的行数
switch (uriMatcher.match(uri)) {
case 1:
num=db.update("person", values,selection,selectionArgs);
break;
case 2:
long id=ContentUris.parseId(uri);
if(selection==null){
selection="id="+id;
}else{
selection="id=+"+id+"and ("+selection+")";
}
num=db.update("person", values,selection,selectionArgs);
break;
default:
break;
}
return num;
}
上面的代码定义了一个简单的contentprovider,并向外部提供了对数据库中person表的CURD方法。下边代码表明应用程序如何使用自定义的contentprovider。
```‘
ContentResolver cr=getContentResolver();
//增加数据
ContentValues values=new ContentValues();
values.put("name", "Tom");
values.put("age", 22);
cr.insert(MyContentProvider.contentUri, values);
values.clear();
values.put("name", "Tony");
values.put("age", 25);
cr.insert(MyContentProvider.contentUri, values);
//查询所有数据
Uri uri
=Uri.parse("content://com.exam.demodata.utils.MyContentProvider/person/3");
// uri=ContentUris.withAppendedId(uri, 3);除了上边在URI中指定查询的id以外,还可以这种方式
Cursor cursor=cr.query(uri, null, null, null, null);
if(cursor.moveToFirst()){
do{
Log.i("info", "id="+cursor.getInt(0)+" name="+cursor.getString(1)+" age="+cursor.getInt(2));
}while(cursor.moveToNext());
//注意,这里使用do_while()循环
cursor.close();
}
- xml中注册provider遇到的问题
首先看注册的代码:
<provider
android:name="com.exam.demodata.utils.MyContentProvider"
android:authorities="com.exam.demodata.utils.MyContentProvider"
android:exported="true"></provider>
注意到上面的注册,比起一般的注册多了一个exported属性;此时可能会出现如下警告:
Exported content providers can provide access to potentially sensitive data。
解释及解决办法
> 1,If you just want the content provider to be accessed internally from within your app, simply add **android:exported="false"** into the node in the manifest.
设置为false的目的: The provider is not available to other applications. Set android:exported="false" to limit access to the provider to your applications.Only applications that have the same user ID (UID) as the provider will have access to it.
2,Also if you are sure that you want to allow external access to your content provider and silence the warning add
**tools:ignore="ExportedContentProvider"** into the node in the manifest.
e.g.
<provider
tools:ignore="ExportedContentProvider"
android:exported="true"
android:name="Contentprovider"
android:authorities="umb.con.apps.vid" />(愚蠢的我总算大致弄明白这个问题了)