在网上很难找到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") @Overridepublic void onChange(boolean selfChange, Uri uri) {// TODO Auto-generated method stubsuper.onChange(selfChange, uri);Log.i(TAG, "DB Chang " + uri.toString());}@Overridepublic 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/开头*/@Overridepublic 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());}}@Overridepublic boolean onCreate() {mSQLiteDatabaseOpenHelper=new SQLiteDatabaseOpenHelper(getContext());return true;}/*** 插入操作:* 插入操作只有一种可能:向一张表中插入* 返回结果为新增记录对应的Uri* 方法db.insert()返回结果为新增记录对应的主键值*/@Overridepublic 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());}}/*** 更新操作:* 更新操作有两种可能:更新一张表或者更新某条数据* 在更新某条数据时原理类似于查询某条数据,见下.*/@Overridepublic 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;}/*** 删除操作:* 删除操作有两种可能:删除一张表或者删除某条数据* 在删除某条数据时原理类似于查询某条数据,见下.*/@Overridepublic 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查询条件.** 详细代码见下.*/@Overridepublic 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;@Overrideprotected 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);}@Overrideprotected void onDestroy() {super.onDestroy();if (mContentObserverSubClass!=null) {this.getContentResolver().unregisterContentObserver(mContentObserverSubClass);}}// 自定义一个内容观察者ContentObserverprivate class ContentObserverSubClass extends ContentObserver {public ContentObserverSubClass(Handler handler) {super(handler);}//采用时间戳避免多次调用onChange( )@Overridepublic 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(时间间隔过短,忽略此次更新);}}@Overridepublic boolean deliverSelfNotifications() {return true;}}private class ClickListenerImpl implements OnClickListener {@Overridepublic 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);}
本文详细介绍ContentObserver的使用方法,包括创建ContentObserver实例、监听自定义Uri及如何通过ContentProvider通知数据变化。

被折叠的 条评论
为什么被折叠?



