在网上很难找到ContentObserver的使用说明,大多都是监控系统的数据库的变化,或者很多文章都是ContentProvider和ContentObserver一起说明,导致内容很多,很乱。那问题来了,我们如何新建自己的ContentObserver来观察我们自己的数据呢,我们又如何用ContentObserver来观察数据库的每一行呢。
所有这里我们先单独说明一ContentObserver的使用,ContentObserver完全是一个独立的东西,使用上和ContentProvider没有一点关系。
ContentObserver(内容观察者):监听指定的Uri,以达到监听数据库的变化,更准确地说,对观察者进行通知。
1. 首先,我们要一个Uri,用来监听和发送通知
Uri uri = Uri.parse("content://cc.along.car/table" + "column");
2. 然后创建ContentObserver来监听这个Uri
Uri uri = Uri.parse("content://cc.along.car/table" + "column");
ContentObserver observer = new ContentObserver(new Handler()) {
@SuppressLint("NewApi") @Override
public void onChange(boolean selfChange, Uri uri) {
// TODO Auto-generated method stub
super.onChange(selfChange, uri);
Log.i(TAG, "DB Chang " + uri.toString());
}
@Override
public void onChange(boolean selfChange) {
Log.i(TAG, "DB Chang");
super.onChange(selfChange);
}
};
3. 在需要的地方对这个Uri发出通知,如数据库的insert,updata等操作完后进行通知
Uri uri = Uri . parse ( "content://cc.along.car/table" + "column" );
context.getContentResolver().notifyChange(uri, null);
就这样的三步,就可以进行信息的通知了,接下来需要对数据进行怎么样的通知就建立怎么样的Uri即可
这样看ContentProvider和ContentObserver联合使用的例子就不会那么头疼了,记住,这两者并没有任命的必要联系
ContentProviderTest.java
/**
* Demo描述:
* 自定义ContentProvider的实现
* ContentProvider主要用于在不同的应用程序之间共享数据,这也是官方推荐的方式.
*
* 注意事项:
* 1 在AndroidManifest.xml中注册ContentProvider时的属性
* android:exported=true表示允许其他应用访问.
* 2 注意*和#这两个符号在Uri中的作用
* 其中*表示匹配任意长度的字符
* 其中#表示匹配任意长度的数据
* 所以:
* 一个能匹配所有表的Uri可以写成:
* content://cn.bs.testcontentprovider/*
* 一个能匹配person表中任意一行的Uri可以写成:
* content://cn.bs.testcontentprovider/person/#
*
*/
public class ContentProviderTest extends ContentProvider {
private SQLiteDatabaseOpenHelper mSQLiteDatabaseOpenHelper;
private final static String AUTHORITY=cn.bs.testcontentprovider;
private static UriMatcher mUriMatcher;
private static final int PERSON_DIR = 0;
private static final int PERSON = 1;
/**
* 利用静态代码块初始化UriMatcher
* 在UriMatcher中包含了多个Uri,每个Uri代表一种操作
* 当调用UriMatcher.match(Uri uri)方法时就会返回该uri对应的code;
* 比如此处的PERSONS和PERSON
*/
static {
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 该URI表示返回所有的person,其中PERSONS为该特定Uri的标识码
mUriMatcher.addURI(AUTHORITY, person, PERSON_DIR);
// 该URI表示返回某一个person,其中PERSON为该特定Uri的标识码
mUriMatcher.addURI(AUTHORITY, person/#, PERSON);
}
/**
* 在自定义ContentProvider中必须覆写getType(Uri uri)方法.
* 该方法用于获取Uri对象所对应的MIME类型.
*
* 一个Uri对应的MIME字符串遵守以下三点:
* 1 必须以vnd开头
* 2 如果该Uri对应的数据可能包含多条记录,那么返回字符串应该以vnd.android.cursor.dir/开头
* 3 如果该Uri对应的数据只包含一条记录,那么返回字符串应该以vnd.android.cursor.item/开头
*/
@Override
public String getType(Uri uri) {
switch (mUriMatcher.match(uri)) {
case PERSON_DIR:
return vnd.android.cursor.dir/+AUTHORITY+.persons;
case PERSON:
return vnd.android.cursor.item/+AUTHORITY+.person;
default:
throw new IllegalArgumentException(unknown uri+uri.toString());
}
}
@Override
public boolean onCreate() {
mSQLiteDatabaseOpenHelper=new SQLiteDatabaseOpenHelper(getContext());
return true;
}
/**
* 插入操作:
* 插入操作只有一种可能:向一张表中插入
* 返回结果为新增记录对应的Uri
* 方法db.insert()返回结果为新增记录对应的主键值
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = mSQLiteDatabaseOpenHelper.getWritableDatabase();
switch (mUriMatcher.match(uri)) {
case PERSON_DIR:
long newId = db.insert(person, name,phone,salary, values);
//向外界通知该ContentProvider里的数据发生了变化 ,以便ContentObserver作出相应
getContext().getContentResolver().notifyChange(uri, null);
return ContentUris.withAppendedId(uri, newId);
default:
throw new IllegalArgumentException(unknown uri + uri.toString());
}
}
/**
* 更新操作:
* 更新操作有两种可能:更新一张表或者更新某条数据
* 在更新某条数据时原理类似于查询某条数据,见下.
*/
@Override
public int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {
SQLiteDatabase db = mSQLiteDatabaseOpenHelper.getWritableDatabase();
int updatedNum = 0;
switch (mUriMatcher.match(uri)) {
// 更新表
case PERSON_DIR:
updatedNum = db.update(person, values, selection, selectionArgs);
break;
// 按照id更新某条数据
case PERSON:
long id = ContentUris.parseId(uri);
String where = personid= + id;
if (selection != null && !.equals(selection.trim())) {
where = selection + and + where;
}
updatedNum = db.update(person, values, where, selectionArgs);
break;
default:
throw new IllegalArgumentException(unknown uri + uri.toString());
}
//向外界通知该ContentProvider里的数据发生了变化 ,以便ContentObserver作出相应
getContext().getContentResolver().notifyChange(uri, null);
return updatedNum;
}
/**
* 删除操作:
* 删除操作有两种可能:删除一张表或者删除某条数据
* 在删除某条数据时原理类似于查询某条数据,见下.
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = mSQLiteDatabaseOpenHelper.getWritableDatabase();
int deletedNum = 0;
switch (mUriMatcher.match(uri)) {
// 删除表
case PERSON_DIR:
deletedNum = db.delete(person, selection, selectionArgs);
break;
// 按照id删除某条数据
case PERSON:
long id = ContentUris.parseId(uri);
String where = personid= + id;
if (selection != null && !.equals(selection.trim())) {
where = selection + and + where;
}
deletedNum = db.delete(person, where, selectionArgs);
break;
default:
throw new IllegalArgumentException(unknown uri + uri.toString());
}
//向外界通知该ContentProvider里的数据发生了变化 ,以便ContentObserver作出相应
getContext().getContentResolver().notifyChange(uri, null);
return deletedNum;
}
/**
* 查询操作:
* 查询操作有两种可能:查询一张表或者查询某条数据
*
* 注意事项:
* 在查询某条数据时要注意--因为此处是按照personid来查询
* 某条数据,但是同时可能还有其他限制.例如:
* 要求personid为2且name为xiaoming1
* 所以在查询时分为两步:
* 第一步:
* 解析出personid放入where查询条件
* 第二步:
* 判断是否有其他限制(如name),若有则将其组拼到where查询条件.
*
* 详细代码见下.
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = mSQLiteDatabaseOpenHelper.getWritableDatabase();
Cursor cursor =null;
switch (mUriMatcher.match(uri)) {
// 查询表
case PERSON_DIR:
cursor = db.query(person, projection, selection, selectionArgs,null, null, sortOrder);
break;
// 按照id查询某条数据
case PERSON:
// 第一步:
long id = ContentUris.parseId(uri);
String where = personid= + id;
// 第二步:
if (selection != null && !.equals(selection.trim())) {
where = selection + and + where;
}
cursor = db.query(person, projection, where, selectionArgs, null, null, sortOrder);
break;
default:
throw new IllegalArgumentException(unknown uri + uri.toString());
}
return cursor;
}
}
AndroidManifest.xml
<!--?xml version=1.0 encoding=utf-8?-->
<manifest android:versioncode="1" android:versionname="1.0" package="cn.testcontentprovider" xmlns:android="http://schemas.android.com/apk/res/android">
<uses-sdk android:minsdkversion="8" android:targetsdkversion="8">
<uses-permission android:name="android.permission.INTERNET">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS">
<intent-filter>
<category android:name="android.intent.category.LAUNCHER">
</category></action></intent-filter>
</activity>
<provider android:authorities="cn.bs.testcontentprovider" android:exported="true" android:name="cn.testcontentprovider.ContentProviderTest">
</provider></application>
</uses-permission></uses-permission></uses-permission></uses-permission></uses-sdk></manifest>
MainActivity.java
/**
* Demo描述:
* 应用A(TestBaidu)调用另外一个应用(TestContentProvider)中的自定义ContentProvider,即:
* 1 自定义ContentProvider的使用
* 2 其它应用调用该ContentProvider
* 3 ContentObserver的使用
*
* 备注说明:
* 1 该例子在以前版本的基础上整理了代码
* 2 该例子在以前版本的基础上融合了ContentObserver的使用
* 利用ContentObserver随时监听ContentProvider的数据变化.
* 为实现该功能需要在自定义的ContentProvider的insert(),update(),delete()
* 方法中调用getContext().getContentResolver().notifyChange(uri, null);
* 向外界通知该ContentProvider里的数据发生了变化 ,以便ContentObserver作出相应
*
* 测试方法:
* 1 依次测试ContentProvider的增查删改(注意该顺序)!!
* 2 其它应用查询该ContentProvider的数据
*
*/
public class MainActivity extends Activity {
private Button mAddButton;
private Button mDeleteButton;
private Button mUpdateButton;
private Button mQueryButton;
private Button mTypeButton;
private long lastTime=0;
private ContentResolver mContentResolver;
private ContentObserverSubClass mContentObserverSubClass;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
init();
initContentObserver();
}
private void init() {
mContentResolver=this.getContentResolver();
mAddButton=(Button) findViewById(R.id.addButton);
mAddButton.setOnClickListener(new ClickListenerImpl());
mDeleteButton=(Button) findViewById(R.id.deleteButton);
mDeleteButton.setOnClickListener(new ClickListenerImpl());
mUpdateButton=(Button) findViewById(R.id.updateButton);
mUpdateButton.setOnClickListener(new ClickListenerImpl());
mQueryButton=(Button) findViewById(R.id.queryButton);
mQueryButton.setOnClickListener(new ClickListenerImpl());
mTypeButton=(Button) findViewById(R.id.typeButton);
mTypeButton.setOnClickListener(new ClickListenerImpl());
}
// 注册一个针对ContentProvider的ContentObserver用来观察内容提供者的数据变化
private void initContentObserver() {
Uri uri = Uri.parse(content://cn.bs.testcontentprovider/person);
mContentObserverSubClass=new ContentObserverSubClass(new Handler());
this.getContentResolver().registerContentObserver(uri, true,mContentObserverSubClass);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mContentObserverSubClass!=null) {
this.getContentResolver().unregisterContentObserver(mContentObserverSubClass);
}
}
// 自定义一个内容观察者ContentObserver
private class ContentObserverSubClass extends ContentObserver {
public ContentObserverSubClass(Handler handler) {
super(handler);
}
//采用时间戳避免多次调用onChange( )
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
System.out.println(ContentObserver onChange() selfChange=+ selfChange);
if (System.currentTimeMillis()-lastTime>2000) {
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse(content://cn.bs.testcontentprovider/person);
// 获取最新的一条数据
Cursor cursor = resolver.query(uri, null, null, null,personid desc limit 1);
while (cursor.moveToNext()) {
int personid = cursor.getInt(cursor.getColumnIndex(personid));
System.out.println(内容提供者中的数据发生变化,现数据中第一条数据的personid=+ personid);
}
cursor.close();
lastTime=System.currentTimeMillis();
}else{
System.out.println(时间间隔过短,忽略此次更新);
}
}
@Override
public boolean deliverSelfNotifications() {
return true;
}
}
private class ClickListenerImpl implements OnClickListener {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.addButton:
Person person = null;
for (int i = 0; i < 5; i++) {
person = new Person(xiaoming + i, 9527 + i, (8888 + i));
testInsert(person);
}
break;
case R.id.deleteButton:
testDelete(1);
break;
case R.id.updateButton:
testUpdate(3);
break;
case R.id.queryButton:
// 查询表
// queryFromContentProvider(-1);
// 查询personid=2的数据
testQuery(2);
break;
case R.id.typeButton:
testType();
break;
default:
break;
}
}
}
private void testInsert(Person person) {
ContentValues contentValues=new ContentValues();
contentValues.put(name, person.getName());
contentValues.put(phone, person.getPhone());
contentValues.put(salary,person.getSalary());
Uri insertUri=Uri.parse(content://cn.bs.testcontentprovider/person);
Uri returnUri=mContentResolver.insert(insertUri, contentValues);
System.out.println(新增数据:returnUri=+returnUri);
}
private void testDelete(int index){
Uri uri=Uri.parse(content://cn.bs.testcontentprovider/person/+String.valueOf(index));
mContentResolver.delete(uri, null, null);
}
private void testUpdate(int index){
Uri uri=Uri.parse(content://cn.bs.testcontentprovider/person/+String.valueOf(index));
ContentValues values=new ContentValues();
values.put(name, hanmeimei);
values.put(phone, 1234);
values.put(salary, 333);
mContentResolver.update(uri, values, null, null);
}
private void testQuery(int index) {
Uri uri=null;
if (index<=0) {
//查询表
uri=Uri.parse(content://cn.bs.testcontentprovider/person);
} else {
//按照id查询某条数据
uri=Uri.parse(content://cn.bs.testcontentprovider/person/+String.valueOf(index));
}
//对应上面的:查询表
//Cursor cursor= mContentResolver.query(uri, null, null, null, null);
//对应上面的:查询personid=2的数据
//注意:因为name是varchar字段的,所以应该写作name='xiaoming1'
// 若写成name=xiaoming1查询时会报错
Cursor cursor= mContentResolver.query(uri, null, name='xiaoming1', null, null);
while(cursor.moveToNext()){
int personid=cursor.getInt(cursor.getColumnIndex(personid));
String name=cursor.getString(cursor.getColumnIndex(name));
String phone=cursor.getString(cursor.getColumnIndex(phone));
int salary=cursor.getInt(cursor.getColumnIndex(salary));
System.out.println(查询得到:personid= + personid+,name=+name+,phone=+phone+,salary=+salary);
}
cursor.close();
}
private void testType(){
Uri dirUri=Uri.parse(content://cn.bs.testcontentprovider/person);
String dirType=mContentResolver.getType(dirUri);
System.out.println(dirType:+dirType);
Uri itemUri=Uri.parse(content://cn.bs.testcontentprovider/person/3);
String itemType=mContentResolver.getType(itemUri);
System.out.println(itemType:+itemType);
}