ContentProvider在android中的作用是对外共享应用的私有数据,也就是说可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider对应用中的数据进行添删改查。

关于数据共享,前面的文件操作模式中知道通过指定文件的操作模式为Context.MODE_WORLD_READABLE或Context.MODE_WORLD_WRITEABLE同样也可以对外共享数据。

那么,为何还要使用ContentProvider 对外共享数据呢?
如果采用文件操作模式对外共享数据,数据的访问方式会因数据存储的方式不同而不同,导致数据的访问方式无法统一,
如:采用xml文件对外共享数据,需要进行xml解析才能读取数据;采用sharedpreferences共享数据,需要使用sharedpreferences API读取数据等等。

使用ContentProvider对外共享数据的好处是统一了数据的访问方式

成为ContentProvider

当应用需要通过ContentProvider对外共享数据时,第一步需要继承ContentProvider并重写下面方法:

ContentProvider - PersonContentProvider.java
1
2
3
4
5
6
7
8
public class PersonContentProvider extends ContentProvider{
public boolean onCreate(){}
public Uri insert(Uri uri, ContentValues values){}
public int delete(Uri uri, String selection, String[] selectionArgs){}
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs){}
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder){}
public String getType(Uri uri){}
}

第二步需要在AndroidManifest.xml 的<provider>节点对该ContentProvider进行配置,为了能让其他应用找到该ContentProvider,ContentProvider 采用了authority(主机名/域名)对它进行唯一标识(详见Content URIs)

Manifest - AndroidManifest.xml
1
2
3
4
5
<manifest .... >
<application android:icon="@drawable/icon" android:label="@string/app_name">
<provider android:name=".PersonContentProvider" android:authorities="net.yrom.providers.personprovider"/>
</application>
</manifest>

Tips:Content Provider在<provider>节点中也可以声明一些访问的权限,当访问者没有申请到权限是不能访问provider的。

ContentProvider类主要方法

  • boolean onCreate()
    该方法在ContentProvider创建后就会被调用, Android开机后, ContentProvider在其它应用第一次访问它时才会被创建。
  • Uri insert(Uri uri, ContentValues values)
    该方法用于供外部应用往ContentProvider添加数据。
  • int delete(Uri uri, String selection, String[] selectionArgs)
    该方法用于供外部应用从ContentProvider删除数据。
  • int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
    该方法用于供外部应用更新ContentProvider中的数据。
  • Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
    该方法用于供外部应用从ContentProvider中获取数据。
  • String getType(Uri uri)
    该方法用于返回当前Url所代表数据的MIME类型。

如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头。
例如:要得到所有person记录的Uri为content://net.yrom.provider.personprovider/person,那么返回的MIME类型字符串应该为:“vnd.android.cursor.dir/person”。

如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头。
例如:得到id为10的person记录,Uri为content://net.yrom.provider.personprovider/person/10,那么返回的MIME类型字符串应该为:“vnd.android.cursor.item/person”。

Content URIs

Content Uri是一个ContentProvider的唯一标识
ContentProvider的Content Uri的scheme已经由Android所规定,为:content://

*Uri的操作见UriMatcher和ContentUris篇

##操作Provider提供的数据
当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver类来完成,可以使用Activity中getContentResolver()方法获取ContentResolver实例。

ContentResolver 类提供了与ContentProvider类相同签名的四个方法:

  • Uri insert(Uri uri, ContentValues values)
    该方法用于往ContentProvider添加数据。
  • int delete(Uri uri, String selection, String[] selectionArgs)
    该方法用于从ContentProvider删除数据。
  • int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
    该方法用于更新ContentProvider中的数据。
  • Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
    该方法用于从ContentProvider中获取数据。

这些方法的第一个参数为Content Uri

###对ContentProvider的数据进行添删改查
eg.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
ContentResolver resolver =  getContentResolver();
//定义Content provider的content uri
Uri uri = Uri.parse("content://net.yrom.provider.personprovider/person");

//添加一条记录
ContentValues values = new ContentValues();
values.put("name", "yrom");
values.put("age", 25);
resolver.insert(uri, values);

//获取person表中所有记录
Cursor cursor = resolver.query(uri, null, null, null, "personid desc");
while(cursor.moveToNext()){
Log.i("ContentTest", "personid="+ cursor.getInt(0)+
",name="+ cursor.getString(1));
}

//把id为1的记录的name字段值更改新为liming
ContentValues updateValues = new ContentValues();
updateValues.put("name", "liming");
Uri updateIdUri = ContentUris.withAppendedId(uri, 1);
resolver.update(updateIdUri, updateValues, null, null);

//删除id为2的记录
Uri deleteIdUri = ContentUris.withAppendedId(uri, 2);
resolver.delete(deleteIdUri, null, null);

###监听ContentProvider中数据的变化

如果ContentProvider的访问者需要知道ContentProvider中的数据发生了变化,可以在ContentProvider 发生数据变化时调用getContentResolver().notifyChange(uri, null)来通知注册在此URI上的访问者
eg.

ContentProvider - PersonContentProvider.java
1
2
3
4
5
6
7
public class PersonContentProvider extends ContentProvider {
//...
public Uri insert(Uri uri, ContentValues values) {
db.insert("person", "personid", values);
getContext().getContentResolver().notifyChange(uri, null);//通知关心此URI数据的访问者
}
}

如果ContentProvider的访问者需要得到数据变化通知,必须使用ContentObserver对数据(数据采用uri描述)进行监听,当监听到数据变化通知时,系统就会调用ContentObserver的onChange()方法:

1
2
3
4
5
6
7
8
9
10
getContentResolver().registerContentObserver(
Uri.parse("content://net.yrom.provider.personprovider/person"),
true, new PersonObserver(new Handler()));
public class PersonObserver extends ContentObserver{
public PersonObserver(Handler handler) {
super(handler);
}
public void onChange(boolean selfChange) {
//此处可以进行相应的业务处理
}

eg.监听用户用户的短信:
使用系统自带的短信程序发送短信时,程序会通过ContentProvider把短信保存进数据库,并且发出一个数据变化通知,使用ContentObserver对数据变化进行监听,在用户发送短信时,就会被ContentObserver监听到短信:
注册监听:

1
getContentResolver().registerContentObserver(Uri.parse("content://sms"),  true, new SmsObserver(new Handler()));

监听类:

ContentObserver - SmsObserver.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private final class SmsObserver extends ContentObserver{
public SmsObserver(Handler handler) {
super(handler);
}
public void onChange(boolean selfChange) {
//查询发送箱中的短信(处于正在发送状态的短信放在发送箱)
//关心的URI为content://sms/outbox
Cursor cursor = getContentResolver().query(Uri.parse("content://sms/outbox"),null, null, null, null);
while(cursor.moveToNext()){
StringBuilder sb = new StringBuilder();
sb.append("_id=").append(cursor.getInt(cursor.getColumnIndex("_id")));
sb.append(",address=").append(cursor.getString(cursor.getColumnIndex("address")));
sb.append(";body=").append(cursor.getString(cursor.getColumnIndex("body")));
sb.append(";time=").append(cursor.getLong(cursor.getColumnIndex("date")));
Log.i("ReceiveSendSMS", sb.toString());
}
}
}