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

    做即时通讯时:首先需要把聊天信息插入到数据库中、然后才展现出来,如何对数据库进行监听呐,如果 开启一个线程大量的查询某个 数据库 值发送了变化,导致的开销很大,可以使用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




  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值