通过ContentObserver监听数据库某表的增加、删除、更新动作,实现listView异步单项Item的刷新

如何使用ContentObserver

使用ContentObserver的步骤:
1.继承ContentObserver,重载父类的构造方法,实现onChange方法,可根据其中的Uri参数区分不同的动作
2. 注册内容观察者。
context.getContentResolover().registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)
功能:为指定的Uri注册一个ContentObserver派生类实例,当给定的Uri发生改变时,回调该实例对象处理。
参数:uri 需要观察的Uri(如:content://example.message/messages 为监听messages表)
notifyForDescendents 为false 表示精确匹配,即只匹配该Uri
为true 表示可以同时匹配其派生的Uri
observer 为自己实现的ContentObserver
注意:此处notifyForDescendents值设置为true,才能做区分增加、删除等动作。
3. 在退出或不需要监听的时候,执行反注册:如在onDestroy()方法中调用

public final void unregisterContentObserver(ContentObserver
observer)
功能:取消对给定Uri的观察
参数: observer ContentObserver的派生类实例

参考代码如下:

private class MessageObserver extends ContentObserver {// 自定义ContentObserver

        public MessageObserver(Handler handler) {
            super(handler);
        }

        @SuppressLint("NewApi") @Override
        public void onChange(boolean selfChange, Uri uri) {
            Log.d(TAG, " self change " + selfChange + " Uri " + uri);
            // parse Uri, insert, delete, update
            int operation = getOperationTypeByUri(uri);//根据Uri获取动作类型(增加、删除等)
            mCurrentMessageId = getIdByUri(uri);//根据Uri获取改变的mID
            Log.d(TAG, "--operation:"+operation+"--mCurrentMessageId:"+mCurrentMessageId);
            doRefreshByOperation(operation,mCurrentMessageId);  //执行数据更新操作  
            super.onChange(selfChange, uri);
        }

    }
    -----------
    //根据Uri获取动作类型
    private int getOperationTypeByUri(Uri uri){
        int operationType = -1;
        String strUri = uri.toString();
        int endIndex = strUri.lastIndexOf("/");
        int beginIndex = endIndex - 6;// insert/update/delete string-size is 6
        String type = strUri.substring(beginIndex, endIndex);
        Log.d(TAG, "--getOperationTypeByUri--type:"+type);
        if(!TextUtils.isEmpty(type)){
            if("insert".equals(type)){
                operationType = OPERATION_INSERT;
            }else if("update".equals(type)){
                operationType = OPERATION_UPDATE;
            }else if("delete".equals(type)){
                operationType = OPERATION_DELETE;
            }
        }
        Log.d(TAG, "--getOperationTypeByUri--operationType:"+operationType);
        return operationType;
    }
    ---------------
    //根据Uri获取改变的mID
    private String getIdByUri(Uri uri){
        String id = "";
        if(null == uri){
            Log.e(TAG, "--getIdbyUri--uri is null, return");
            return id;
        }
        String strUri = uri.toString();
        int lastIndex = strUri.lastIndexOf("/");
        id = strUri.substring(lastIndex + 1, strUri.length());
        Log.d(TAG, "--getIdByUri--id:"+id);
        return id;
    }
    ---------------
    //根据数据库动作类型,执行不同的操作(此处实现的是聊天页面listView异步刷新单个Item)
    private void doRefreshByOperation(int operation, String id){
        if(null == mSmsProviderHelp){
            Log.e(TAG, "--doRefreshByOperation mSmsProviderHelp is null, return");
            return;
        }
        switch (operation) {
        case OPERATION_INSERT:
            mSmsProviderHelp.startQueryMessageById(mQueryHandler, SmsProviderHelp.MESSAGES_INSERT_BY_ID_TOKEN, id,mPhoneNumber);//根据mID查询单条数据
            break;
        case OPERATION_UPDATE:
            mSmsProviderHelp.startQueryMessageById(mQueryHandler, SmsProviderHelp.MESSAGES_UPDATE_BY_ID_TOKEN, id,mPhoneNumber);//根据mID查询单条数据
            break;
        case OPERATION_DELETE:
            // delete one from arraylist
            int mID = -1;
            try {
                mID = Integer.parseInt(id);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
            int deletePosi = mSparseArray.get(mID, -1);
            Log.d(TAG, "--deletePosi:"+deletePosi+"--mID:"+mID);
            if(deletePosi > -1 && null != mDataList && deletePosi < mDataList.size()){
                mDataList.remove(deletePosi);
                mMessageAdapter.notifyDataSetChanged();
            }
            break;

        default:
            break;
        }
    }

实现ContentProvider,对数据库的增加、删除、更新动作进行通知

上面实现的ContentObserver,只是进行监听,当动作变化时需要执行的逻辑放在onChange中。
那么,如何进行通知呢?
答案就是:继承ContentProvider,需要自己实现其中的insert、delete、update方法,发送不同的Uri出去。
参考代码如下:

public class SmsProvider extends ContentProvider {
@Override
    public int delete(Uri url, String where, String[] whereArgs) {
    String table = TABLE_SMS;
        int count;
        int match = sURLMatcher.match(url);
        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
        switch (match) {
        case ACTION_SMS:
            table = TABLE_SMS;
            break;  
        case ACTION_THREAD:
            table = TABLE_THREAD;                       
            break;
        default:
            throw new IllegalArgumentException("Unknown URL" +url.toString());
        }
        count = db.delete(table, where, whereArgs);
        if (count > 0) {
            String rowId = "";
            if(null != whereArgs){
                rowId  = whereArgs[0];
            }
            Uri deleteUri = Uri.withAppendedPath(url, "delete/"+rowId);//此处为重点,构造了一个Uri发送出去
            Log.d(TAG, "--deleteUri:"+deleteUri);
            notifyChange(deleteUri);
        }
        return count;
    }

private void notifyChange(Uri uri) {
        ContentResolver cr = getContext().getContentResolver();
        cr.notifyChange(uri, null);
    }
    @Override
    public Uri insert(Uri url, ContentValues initialValues) {
    ...
    rowID = db.insert(table, "body", values);
        if (rowID > 0) {
            Uri insertUri = Uri.withAppendedPath(url, "insert/"+rowID);//此处为重点,构造了一个Uri发送出去
            Log.d(TAG, "--insertUrl:"+insertUri);
            notifyChange(insertUri);
            Uri uri = Uri.parse("content://" + table + "/" + rowID);
            Log.d(TAG, "insert " + uri + " succeeded");     
            return uri;
        }

@Override
    public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
    ...
     count = db.update(table, values, where, whereArgs);

        if (count > 0) { 
            Log.d(TAG, "update " + url + " succeeded"+"values"+values.toString()+"where"+where); 
            String rowId = "";
            if(null != whereArgs){
                rowId  = whereArgs[0];
            }
            Uri updateUri = Uri.withAppendedPath(url, "update/"+rowId);//此处为重点,构造了一个Uri发送出去
            Log.d(TAG, "--updateUri:"+updateUri);
            notifyChange(updateUri);
        }
        return count;

代码描述:
在delete方法中,判断删除成功时,构造带有“delete/mID”的Uri.
在insert方法中,判断增加成功时,构造带有“insert/mID”的Uri
在update方法中,判断更新成功时,构造带有“update/mID”的Uri
最后通过调用
ContentResolver cr = getContext().getContentResolver();
cr.notifyChange(uri, null);// null表示所有的observer都能收到
发送通知出去。

可以实现的功能

通过Uri区分不同的数据库动作后。可以根据需要实现不同的功能。
比如:聊天消息页面的异步单项Item刷新流程如下:
流程:
1. 区分message insert update delete 动作,通过messageId查询单条数据
2. 在onchange方法中根据URI区分各种动作。
3. 维护一个map保存position和messageId优化性能
4. 对onChange方法后的刷新逻辑做判断。
@1若为Insert操作,总结起来就是:把数据插入到最后面,调用notifydatasetchanged.
@2.为update操作,根据messageId,单独查询该消息,查询完成后,把该messageId对应的Item数据更新,调用notifidatasetchanged.
@3.为delete操作,根据messageId,找到对应的position,从datalist中remove该Item,调用notifidatasetchanged
另外,可参考另一篇文章:通过AsyncQueryHandler异步对数据库进行增删查操作

若对你还有点用处,那就是本篇文章的意义所在。谢谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值