天天看點

Android ContentProvider和ContentObserver 監控資料庫變化 簡單使用說明

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