registerContentObserver() on raw SQLite Cursor

前端 未结 2 1701
孤独总比滥情好
孤独总比滥情好 2020-12-24 14:50

All of the examples I\'ve seen of using registerContentObserver() do so through a ContentProvider interface. But Cursor has a registerContent

相关标签:
2条回答
  • 2020-12-24 15:36

    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.java frameworks/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.

    0 讨论(0)
  • 2020-12-24 15:37

    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();
    }
    
    @Override
    public 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:

    @Override
    protected 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.

    0 讨论(0)
提交回复
热议问题