Android ContentObserver实现数据库监听

本文只是写了一个监听数据库插入操作的小例子,涉及到数据库操作,以及ContentProvider向外提供数据用博客来记录一下.
首先我们要创建一个ContentObserver来监听,并将其进行注册

    /**
     * 定义一个内容观察者
     */
    private ContentObserver mContentObserver = new ContentObserver(mHandler) {
        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
        }
    };
    getContentResolver().registerContentObserver(MyContentProvider.CONTENT_URI,true,mContentObserver);

因为是监听数据库,我们必须得创建一个数据库,直接看代码,简单创建一个,数据库名为csm,表名为csm_test_database该表中有三个字段分别是_id,_name,_age

/**
 * 自己撞创建一个数据库
 * 数据库中存在表csm_test_database
 * 表中有三个字段,分别如下:
 * _id,_name,_age
 */
public class MyDatabaseHelper extends SQLiteOpenHelper {
    private SQLiteDatabase mSQLiteDatabase;
    private static final int DB_VERSION = 2;
    private static final String DB_NAME = "csm";
    private static final String TABLE_NAME = "csm_test_database";
    public static final String COLUMN_ID = "_id";
    public static final String COLUMN_NAME = "_name";
    public static final String COLUMN_AGE = "_age";
    private static final String CREATE_TABLE_SQL = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
            COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
            COLUMN_NAME + " TEXT ," +
            COLUMN_AGE + " INTEGER ) ";

    private static MyDatabaseHelper mMyDatabaseHelper;

    private SQLiteDatabase mDb;

    public static MyDatabaseHelper getInstance(final Context context) {
        if (mMyDatabaseHelper == null) {
            synchronized (MyDatabaseHelper.class) {
                if (mMyDatabaseHelper == null) {
                    mMyDatabaseHelper = new MyDatabaseHelper(context);
                }
            }
        }

        return mMyDatabaseHelper;
    }


    private MyDatabaseHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_TABLE_SQL);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); //$NON-NLS-1$
        onCreate(db);
    }

    /**
     * 插入一个数据库项
     * @param values
     * @return
     */
    public long insert(ContentValues values)
    {
        mDb = mMyDatabaseHelper.getWritableDatabase();
        return mDb.insert(TABLE_NAME,null,values);
    }

    public void deleteAll()
    {
        mDb = mMyDatabaseHelper.getWritableDatabase();
        mDb.delete(TABLE_NAME,null,null);
    }
}

观察者注册好了,数据库创建好了并且实现的insert操作,是不是在insert操作中发送一个通知给观察者就好了?调用如下代码:

getContext().getContentResolver().notifyChange(CONTENT_URI,null);

其实不然,这样是没法正确的发送到观察者.这里有一个问题就是CONTENT_URI是什么,CONTENT_URI对应的是一个资源库,而这里的资源库正是我们的数据库.但是在以上的实现中我们看不出来CONTENT_URI跟我们的数据库有什么关系.所以我们要为这个数据库关联上一个CONTENT_URI.这里就用ContentProvider来建立CONTENT_URI和数据库的关系.ContentProvider是和CONTEN_URI直接关联的,一个ContentProvider对应一个CONTENT_URI,如果通过CONTENT_URI的方式获取资源,会先调用到ContentProvider方法,然后在通过ContentProvider的中方法再去获取资源操作(此例中的资源是数据库).所以这个操作的流程是CONTENT_URI -> ContentProvider -> Database. CONTENT_URI是通过ContentProvider与数据建立起来联系,他们之间并没有直接联系.所以我们要实现一个ContentProvider. 如下:

/**
 * MyContentProvider可以看成是对数据库操作的一个包装
 * 通过直指定的URI,调用getContext().getContentResolver().insert()方法
 * 就会调用到此处的public Uri insert(Uri uri, ContentValues values)
 * 此时我们再转化为对数据库的操作,如本类中的insert方法
 *
 * 咱么监听到数据库变化的呢?
 * 其实在数据库发生变化的时候是我们手动调用的notifyChange方法
 */
public class MyContentProvider extends ContentProvider {
    private static final String AUTHORITIES = "com.csm.provider";
    private static final String PATH = "test";
    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITIES);
    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    private static final int URI_CODE = 0x123;

    static
    {
        sUriMatcher.addURI(AUTHORITIES,PATH,URI_CODE);
    }

    @Override
    public boolean onCreate() {
        return false;
    }

    @Nullable
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        return null;
    }

    @Nullable
    @Override
    public String getType(Uri uri) {
        return null;
    }

    /**
     * 当通过getContentResolver().insert(MyContentProvider.CONTENT_URI,values)的方式插入数据库
     * 会调用到此处,此时我们将其插入数据库并且
     * 通知观察者数据 发生变化
     * @param uri 要操作的资源uri
     * @param values 要插入的内容
     * @return
     */
    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        MyDatabaseHelper db = MyDatabaseHelper.getInstance(getContext());
        long row = db.insert(values);
        if(row != -1)
        {
            getContext().getContentResolver().notifyChange(CONTENT_URI,null);
        }
        return CONTENT_URI;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }
}

并且需要在AndroidManifest.xml中声明该ContentProvider

        <provider
            android:authorities="com.csm.provider"
            android:name="com.example.rander.myapplication.MyContentProvider"
            />

上面的android:authorities是ContentProvider的唯一标识,在代码中我们为之构建的Content uri是

CONTENT_URI = Uri.parse("content://" + AUTHORITIES)

转化为字符串就是

content://com.csm.provider

根据URI的格式

scheme://host:port/path

host对应的是com.csm.provider,也就是AUTHORITIES,它唯一标示了我们这个MyContentProvider,所以访问content://com.csm.provider的资源就是调用MyContentProvider的方法获取资源.他们的关系就这样建立起来了.
所以在执行

getContentResolver().insert(MyContentProvider.CONTENT_URI,values);

的时候,就会调用MyContentProvider的insert方法,MyContentProvider转而对数据库进行操作,如果插入成功则会调用

getContext().getContentResolver().notifyChange(CONTENT_URI,null);

通知观察CONTENT_URI变化的观察者,也就是我们第一步注册的观察者.这样就监听到了数据库的变化了.
使用的例子就是

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private MyDatabaseHelper mMyDatabaseHelper;

    private Handler mHandler = new Handler(Looper.getMainLooper());
    /**
     * 定义一个内容观察者
     */
    private ContentObserver mContentObserver = new ContentObserver(mHandler) {
        @Override
        public void onChange(boolean selfChange) {
            Log.e(TAG, "onChange: =======+>>>>>>>>>>>>selfChange is " + selfChange);
            super.onChange(selfChange);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mMyDatabaseHelper = MyDatabaseHelper.getInstance(this);

        //注册内容观察者,它监听的是MyContentProvider.CONTENT_URI,对应的字符串是"content://com.csm.provider"
        //根据Uri的格式如下
        //scheme://host:port/path
        //该Uri对应的host为com.csm.provider,也就是我们在AndroidManifest.xml自定义MyContentProvider的authorities
        //所以该CONTENT_URI对应的就是MyContentProvider提供的资源
        getContentResolver().registerContentObserver(MyContentProvider.CONTENT_URI,true,mContentObserver);

        /**
         * 顺循环想数据库中插入数据
         * getContentResolver().insert第一个参数为MyContentProvider.CONTENT_URI,
         * 因为MyContentProvider.CONTENT_URI对应的是MyContentProvider的资源,所以
         * 该insert的方法会调用到MyContentProvider的insert
         * 在insert方法里面再进行数据库操作.
         * 插入成功的话,再调用notifyChange方法通知观察者
         */
        new Thread()
        {
            @Override
            public void run() {
                int i = 0;
                mMyDatabaseHelper.deleteAll();
                while (true)
                {
                    ContentValues values = new ContentValues();
                    values.put(MyDatabaseHelper.COLUMN_ID,i);
                    values.put(MyDatabaseHelper.COLUMN_NAME,i+"");
                    getContentResolver().insert(MyContentProvider.CONTENT_URI,values);
                    try {
                        sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    i++;
                }
            }
        }.start();
    }
}

每隔1s中向MyContentProvider.CONTENT_URI资源库中插入一条数据,会看到ContentObserver的onChange每隔一秒中回调一次.

05-26 14:21:02.785 12293-12293/com.example.rander.myapplication E/MainActivity: onChange: =======+>>>>>>>>>>>>selfChange is false
05-26 14:21:02.811 12293-12293/com.example.rander.myapplication E/MainActivity: onChange: =======+>>>>>>>>>>>>selfChange is false
05-26 14:21:02.834 12293-12293/com.example.rander.myapplication E/MainActivity: onChange: =======+>>>>>>>>>>>>selfChange is false
05-26 14:21:02.861 12293-12293/com.example.rander.myapplication E/MainActivity: onChange: =======+>>>>>>>>>>>>selfChange is false
05-26 14:21:02.886 12293-12293/com.example.rander.myapplication E/MainActivity: onChange: =======+>>>>>>>>>>>>selfChange is false
05-26 14:21:02.906 12293-12293/com.example.rander.myapplication E/MainActivity: onChange: =======+>>>>>>>>>>>>selfChange is false
05-26 14:21:02.926 12293-12293/com.example.rander.myapplication E/MainActivity: onChange: =======+>>>>>>>>>>>>selfChange is false
05-26 14:21:02.943 12293-12293/com.example.rander.myapplication E/MainActivity: onChange: =======+>>>>>>>>>>>>selfChange is false
05-26 14:21:02.961 12293-12293/com.example.rander.myapplication E/MainActivity: onChange: =======+>>>>>>>>>>>>selfChange is false

Demo link如下
https://github.com/shuangmin/AndroidContentObserverDemo

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值