天天看点

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);              }