android 使用ContentObserve对数据库的值变化进行监听

来源:互联网 发布:小米抢购软件手机版 编辑:程序博客网 时间:2024/05/16 01:15
    做即时通讯时:首先需要把聊天信息插入到数据库中、然后才展现出来,如何对数据库进行监听呐,如果开启一个线程大量的查询某个数据库值发送了变化,导致的开销很大,可以使用ContentObserve对数据库进行监听。
需要了解的知识点:
1、Content Provider(内容提供者)
            其中为了解析URI提供了一个工具类UriMather,通过addURI()的可以注册新的uri。们可以通过这些不同的 Uri来查询不同的结果。根据Uri返回的结果,Uri Type可以分为:返回多条数据的Uri、返回单条数据的Uri。利用context.getContentResolover()获得ContentResolove对象,接着调用registerContentObserver()法去注册内容观察者,值得注意的是在界面销毁的时候不要忘记反注册,例如在Activity的onDe story方法中不要忘记调用方法:unregisterContentObserver()去取消注册。
2、ContentObserve
          ContentObserver——内容观察者,目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理。

           (1)构造方法 public void ContentObserver(Handler handler)  

           参数:Handler对象,在主线程中可以通过它修改UI。
           (2)常见方法    void onChange(boolean selfChange)
           参数:回调后,其值一般为false,该参数意义不大。
3、public final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)

             功能:为指定的Uri注册一个ContentObserver派生类实例,当给定的Uri发生改变时,回调该实例对象去处理。

             参数:uri  需要观察的Uri(需要在UriMatcher里注册,否则该Uri也没有意义, notifyForDescendents  为false 表示精确匹配,即只匹配该Uri,如果为true 表示可以同时匹配其派生的Uri,举例如下:

                          假设UriMatcher 里注册的Uri共有一下类型:

                                1 、content://com.qin.cb/student (学生)

                                2 、content://com.qin.cb/student/# 

                                3、 content://com.qin.cb/student/schoolchild(小学生,派生的Uri)

         假设我们当前需要观察的Uri为content://com.qin.cb/student,如果发生数据变化的 Uri 为 content://com.qin.cb/student/schoolchild ,当notifyForDescendents为 false,那么该ContentObserver会监听不到, 但是当notifyForDescendents 为ture,能捕捉该Uri的数据库变化。

                           observer       ContentObserver的派生类实例 

   4、public final void  unregisterContentObserver(ContentObserver observer)

          功能:取消对给定Uri的观察

          参数: observer ContentObserver的派生类实例

public class MyDataProvider extends ContentProvider  {        // public static final String SCHEME = "test";      public static final String SCHEME = "content"; // 源码里面规定这样写,所以这个地方改变不了        public static final String HOST = "com.zyj";      public static final String PORT = "497393102";      public static final String PATH = "simple";        public static final int ALARMS = 1;      public static final String SHARE_LIST_TYPE = "com.zyj.test.dir/";      public static final int ALARMS_ID = 2;      public static final String SHARE_TYPE = "com.zyj.test.item/";        private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);        private SQLiteOpenHelper mDB = null;        // ===content://com.zyj:497393102/simple      public static final Uri CONTENT_URI = Uri.parse(SCHEME + "://" + HOST + ":" + PORT + "/" + PATH);        // 添加Uri的匹配方式,返回的就是上面自定义的整数类型,1代表操作的是一个批量,2操作的是单独的一个对象      static      {          sURIMatcher.addURI(HOST + ":" + PORT, PATH, ALARMS);          sURIMatcher.addURI(HOST + ":" + PORT, PATH + "/#", ALARMS_ID);      }        @Override      public boolean onCreate()      {          mDB = new MyDB(getContext()); // 获取数据库的引用          return mDB != null;      }        @Override      public String getType(Uri uri)      {          // 得到我们自定义的Uri的类型,看上面你自己的定义          int match = sURIMatcher.match(uri);          switch (match)          {              case ALARMS:              {                  return SHARE_LIST_TYPE;              }              case ALARMS_ID:              {                  return SHARE_TYPE;              }              default:              {                  throw new IllegalArgumentException("Unknown URI: " + uri);              }          }      }        @Override      public Uri insert(Uri uri, ContentValues values)      {          // 首先是看Uri和我们自定义的是否匹配,,匹配则将数据属性插入到数据库中并同志更新          SQLiteDatabase db = mDB.getWritableDatabase();          if (sURIMatcher.match(uri) != ALARMS)          {              throw new IllegalArgumentException("Unknown/Invalid URI " + uri);          }            ContentValues filteredValues = new ContentValues();          filteredValues.put(MyDB.BEAN_ID, values.getAsInteger(MyDB.BEAN_ID));          filteredValues.put(MyDB.MESSAGE, values.getAsString(MyDB.MESSAGE));          filteredValues.put(MyDB.TASK_PROGRESS, values.getAsFloat(MyDB.TASK_PROGRESS));          long rowID = db.insert(MyDB.TABLET, null, filteredValues);          if (rowID != -1)          {              getContext().getContentResolver().notifyChange(uri, null);          }          return CONTENT_URI;      }        @Override      public int delete(Uri uri, String selection, String[] selectionArgs)      {            // 首先是看Uri和我们自定义的是否匹配,,匹配则进行删除            SQLiteDatabase db = mDB.getWritableDatabase();          int count = 0;          int match = sURIMatcher.match(uri);          switch (match)          {              case ALARMS:              case ALARMS_ID:                  String where = null;                  // 这里对selection进行匹配操作,看你传递的是一个批量还是一个单独的文件                  if (selection != null)                  {                      if (match == ALARMS)                      {                          where = "( " + selection + " )";                      }                      else                      {                          where = "( " + selection + " ) AND ";                      }                  }                  else                  {                      where = "";                  }                  if (match == ALARMS_ID)                  {                      // 如果你传递的是一个单独的文件,也就是Uri后面添加了/item的,那么在这里把该值与数据库中的属性段进行比较,返回sql语句中的where                      String segment = uri.getPathSegments().get(1);                      long rowId = Long.parseLong(segment);                      where += " ( " + MyDB.BEAN_ID + " = " + rowId + " ) ";                  }                  count = db.delete(MyDB.TABLET, where, selectionArgs);                  break;              default:                  throw new UnsupportedOperationException("Cannot delete URI: " + uri);          }          getContext().getContentResolver().notifyChange(uri, null);          return count;      }        @Override      public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)      {          // 基本同上了          SQLiteDatabase db = mDB.getWritableDatabase();            int count;          long rowId = 0;            int match = sURIMatcher.match(uri);          switch (match)          {              case ALARMS:              case ALARMS_ID:              {                  String myWhere;                  if (selection != null)                  {                      if (match == ALARMS)                      {                          myWhere = "( " + selection + " )";                      }                      else                      {                          myWhere = "( " + selection + " ) AND ";                      }                  }                  else                  {                      myWhere = "";                  }                  if (match == ALARMS_ID)                  {                      String segment = uri.getPathSegments().get(1);                      rowId = Long.parseLong(segment);                      myWhere += " ( " + MyDB.BEAN_ID + " = " + rowId + " ) ";                  }                    if (values.size() > 0)                  {                      count = db.update(MyDB.TABLET, values, myWhere, selectionArgs);                  }                  else                  {                      count = 0;                  }                  break;              }              default:              {                  throw new UnsupportedOperationException("Cannot update URI: " + uri);              }          }          getContext().getContentResolver().notifyChange(uri, null);            return count;      }        @Override      public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)      {          SQLiteDatabase db = mDB.getReadableDatabase();          SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); //SQLiteQueryBuilder是一个构造SQL查询语句的辅助类            int match = sURIMatcher.match(uri);          switch (match)          {              case ALARMS:              {                  qb.setTables(MyDB.TABLET);                  break;              }              case ALARMS_ID:              {                  qb.setTables(MyDB.TABLET);                  qb.appendWhere(MyDB.BEAN_ID + "=");                  qb.appendWhere(uri.getPathSegments().get(1));                  break;              }              default:                  throw new IllegalArgumentException("Unknown URI: " + uri);          }          Cursor ret = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);            if (ret != null)          {              ret.setNotificationUri(getContext().getContentResolver(), uri);              Log.d("zyj", "created cursor " + ret + " on behalf of ");          }          else          {              Log.d("zyj", "query failed in downloads database");          }          return ret;      }        private static class MyDB extends SQLiteOpenHelper      {            // 这里就是数据库了,数据库字段、名称、表名等...          private static final String DATABASE = "test_database";          public static final String TABLET = "test_table";          public static String ID = "_id";          public static String BEAN_ID = "_bean_id";          public static String MESSAGE = "_message";          public static String TASK_PROGRESS = "_progress";            private SQLiteDatabase mDB = null;            private final String msql = "CREATE TABLE IF NOT EXISTS " + TABLET + "( " + ID                  + " INTEGER PRIMARY KEY AUTOINCREMENT, " + BEAN_ID + " TEXT, " + MESSAGE + " TEXT, " + TASK_PROGRESS                  + " TEXT )";            private MyDB(Context context)          {              super(context, DATABASE, null, 1);          }            @Override          public void onCreate(SQLiteDatabase db)          {              mDB = db;              mDB.execSQL(msql);          }            @Override          public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)          {              // 升级,自己可以去实现          }      }  }  


    MyBean.java一个实例对象

    

public class MyBean  {        public int id = 0;            public String message = null;        public float progress = 0.0f;        public MyBean(int id)      {          this.id = id;      }        }  

     MainActivity.java主界面了

     

public class MainActivity extends Activity  {        TextView mMessage = null;        private ContentObserver mDatabaseListener = null;      private Handler mHand = null;        @Override      protected void onCreate(Bundle savedInstanceState)      {          super.onCreate(savedInstanceState);          setContentView(R.layout.activity_main);            mMessage = (TextView) findViewById(R.id.message);            init();            // 注册数据库的监听,对应的是特定的Uri          getContentResolver().registerContentObserver(MyDataProvider.CONTENT_URI, true, mDatabaseListener);      }        @Override      protected void onDestroy()      {          super.onDestroy();          // 注销掉监听          getContentResolver().unregisterContentObserver(mDatabaseListener);      }        private void init()      {          mHand = new Handler();          // 数据库变动时的回调          mDatabaseListener = new ContentObserver(mHand)          {              @Override              public boolean deliverSelfNotifications()              {                  System.out.println("deliverSelfNotifications ---------------- ");                  return super.deliverSelfNotifications();              }                @Override              public void onChange(boolean selfChange, Uri uri)              {                  System.out.println("onChange ---------------- " + uri.toString());                  super.onChange(selfChange, uri);              }                @Override              public void onChange(boolean selfChange)              {                  System.out.println("onChange ---------------- ...");                  super.onChange(selfChange);              }          };      }        private int count = 0;        public void onViewClick(View view)      {          switch (view.getId())          {              case R.id.add:                  // 插入数据                  ContentValues calues = new ContentValues();                  calues.put("_bean_id", count++);                  calues.put("_message", "AAAAAAAAAAAAAAAAAAAAA");                  calues.put("_progress", 0.0f);                  getContentResolver().insert(MyDataProvider.CONTENT_URI, calues);                  break;              case R.id.del:                  // 如果找不到指定的_bean_id=1、2、3的,则数据库不进行增减,但还是会调用回调方法                  getContentResolver().delete(Uri.parse(MyDataProvider.CONTENT_URI.toString() + "/1"), null, null);                  getContentResolver().delete(Uri.parse(MyDataProvider.CONTENT_URI.toString() + "/2"), null, null);                  getContentResolver().delete(Uri.parse(MyDataProvider.CONTENT_URI.toString() + "/3"), null, null);                  break;              case R.id.modify:                  ContentValues values = new ContentValues();                  values.put("_message", "ZZZZZZZZZZZZZZZZZZZZZ");                  // 这两中方法一样,这样就可以更加明白Uri中在后面添加的/item了数字的意思了                  getContentResolver()                          .update(Uri.parse(MyDataProvider.CONTENT_URI.toString() + "/5"), values, null, null);                  getContentResolver().update(MyDataProvider.CONTENT_URI, values, "_bean_id=?", new String[] { "6" });                  break;              case R.id.query:                  showMessage(getContentResolver().query(MyDataProvider.CONTENT_URI, null, null, null, null));                  break;          }      }        private void showMessage(Cursor c)      {          if (c == null)          {              return;          }          final StringBuffer sb = new StringBuffer();          if (c.getCount() > 0)          {              while (c.moveToNext())              {                  MyBean bean = new MyBean(c.getInt(c.getColumnIndex("_bean_id")));                  bean.message = c.getString(c.getColumnIndex("_message"));                  bean.progress = c.getFloat(c.getColumnIndex("_progress"));                  sb.append(bean.id + "\t\t\t:" + bean.message + "\t\t\t,progress = " + bean.progress + "\n");              }          }            c.close();            mHand.post(new Runnable()          {              public void run()              {                  mMessage.setText(sb.toString());              }          });      }  } 
 


    activity_main.xml    上面就是四个按钮,下面就是一个TextView显示控件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"      xmlns:tools="http://schemas.android.com/tools"      android:layout_width="match_parent"      android:layout_height="match_parent"      android:orientation="vertical"      android:padding="10.0dip" >        <LinearLayout          android:layout_width="fill_parent"          android:layout_height="0dip"          android:layout_weight="1"          android:orientation="vertical" >            <Button              android:id="@+id/add"              android:layout_width="fill_parent"              android:layout_height="wrap_content"              android:onClick="onViewClick"              android:text="Add" />            <Button              android:id="@+id/del"              android:layout_width="fill_parent"              android:layout_height="wrap_content"              android:onClick="onViewClick"              android:text="Delete" />            <Button              android:id="@+id/modify"              android:layout_width="fill_parent"              android:layout_height="wrap_content"              android:onClick="onViewClick"              android:text="Modify" />            <Button              android:id="@+id/query"              android:layout_width="fill_parent"              android:layout_height="wrap_content"              android:onClick="onViewClick"              android:text="Query" />      </LinearLayout>        <ScrollView          android:layout_width="fill_parent"          android:layout_height="0dip"          android:layout_weight="1"          android:maxHeight="20dip" >            <TextView              android:id="@+id/message"              android:layout_width="fill_parent"              android:layout_height="wrap_content"              android:textSize="20sp" />      </ScrollView>    </LinearLayout>  

    最后就是AndroidManifest.xml了,和平时一样的,只不过在里面将你自己定义的ContentProvider写上,类似我的这样:

    <provider
            Android:name="com.example.databasetest.MyDataProvider"
            android:authorities="com.zyj:497393102" />
    上面的authorities属性是一定要写的,它就是上面MyDataProvider.java里面的CONTENT_URI的HOST + ":" + PORT,可以看下面画的,就比较清楚了。


    content://com.example.project:200/folder/subfolder/etc
    \---------/  \---------------------------/ \---/ \--------------------------/
    scheme                 host               port        path
                    \--------------------------------/
                              authority   



    然后就没有了,可以自己运行,感受一下Uri和数据库的监听。

参考:http://blog.csdn.net/qinjuning/article/details/7047607

http://blog.csdn.net/a497393102/article/details/44223219