registerContentObserver() on raw SQLite Cursor registerContentObserver() on raw SQLite Cursor sqlite sqlite

registerContentObserver() on raw SQLite Cursor


This is actually possible.

You have to define an Uri somewhere (a constant maybe).

I would advice to use something like this:

public static final Uri URI_MY_TABLE =     Uri.parse("sqlite://com.example.<your-app-package>/table");

Then when you update the database data you call:

context.getContentResolver().notifyChange(Constants.URI_MY_TABLE, null);

And from the CursorLoader you are writing do something like:

final ForceLoadContentObserver observer;public MyCursorLoader(final Context context, ...) {    super(context);    // ...    this.observer = new ForceLoadContentObserver();}@Overridepublic Cursor loadInBackground() {    SQLiteDatabase db = this.dbHelper.getReadableDatabase();    final Cursor c = queryDatabase(db);    if (c != null) {        // Ensure the cursor window is filled        c.getCount();        // this is to force a reload when the content change        c.registerContentObserver(this.observer);        // this make sure this loader will be notified when        // a notifyChange is called on the URI_MY_TABLE        c.setNotificationUri(getContext().getContentResolver(),            Constants.URI_MY_TABLE);    }    return c;}

The ForceLoadContentObserver is a public static inner class inside the Loader class, if you are using the Support Library it will be android.support.v4.content.Loader.ForceLoadContentObserver

In the same Loader make sure you forceReload if data change:

@Overrideprotected void onStartLoading() {    if (this.cursor != null) {        deliverResult(this.cursor);    }    // this takeContentChanged() is important!    if (takeContentChanged() || this.cursor == null) {        forceLoad();    }}

A good start for your CursorLoader if you do not know how to write it is this: CursorLoader usage without ContentProvider

Your CursorAdapter should now be initialized like this:

public MyCursorAdapter(Context context, Cursor c) {    super(context, c, 0);}

The ContentResolver will take care of notifying observer on the uri you set up.

This is exactly how the ContentProvider works.

If you are not using a Loader you can do the same stuff, the important modifications are:

  • Call ContentResolver.notifyChange(URI,null); when you change the data
  • Call Cursor.setNotificationUri(ContentResolver, URI); when you load the cursor

This way when you register an observer you will be notified when the underling data change.


No takers huh? Well an excellent excuse to dig in myself and figure it out. For those who might be curious after me, if you download the platform source the relevant files to look at are:

frameworks/base/core/java/android/database/sqlite/SQLiteCursor.javaframeworks/base/core/java/android/database/AbstractCursor.java

It looks like the mContentObservable is there to signal different parts of a program running off the cached result set in a Cursor, and the observable is a signal from the cached result set to let consumers know when the cache has been dumped/updated. It doesn't tie into the SQLite implementation to fire events when the underlying datastore is manipulated.

Not exactly surprising, but given the docs I thought maybe I was missing something.