在網上很難找到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); }