How to implement a ContentObserver in a fragment using CursorAdapter and LoaderCallbacks?

android, android-contentprovider, android-cursoradapter, android-cursorloader, contentobserver

Solution

`SqlCursor` already have internal `ContentObserver` and since you're using this `Cursor` implementation there is no need for own `ContentObserver` implementation.

For easy use you can add:

class MyContentProvider extends ContentProvider{
  //rest implementation goes here
  @Override
  public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
    Cursor cursor = null;
    //check uri, select table name based on uri, etc ...
    cursor = db.query(/*.....*/);
    //add this line:
    cursor.setNotificationUri(getContext().getContentResolver(), uri);
    //before you return cursor
    return cursor;
  }
  @Override
  public Uri insert(Uri uri, ContentValues values) {
     /* ... */
       //you can notify only "dir" uri and is should be enough
       getContext().getContentResolver().notifyChange(uri, null);
       return itemUri;
     /* ... */
  }
  //rest of implementation goes here
}

after this, `CursorLoader` internal implementation will take care of reloading/refreshing data ...

Problem

I am using a `CursorAdapter` in a `ListFragment` to load and display a list of comments. ``` public class CommentsFragment extends ListFragment implements LoaderCallbacks<Cursor> { protected Activity mActivity; protected CursorAdapter mAdapter; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mActivity = getActivity(); mAdapter = new CommentsCursorAdapter(mActivity, null, 0); setListAdapter(mAdapter); mActivity.getContentResolver().registerContentObserver(CustomContract.Comments.CONTENT_URI, false, new CommentsObserver()); getLoaderManager().initLoader(0, null, this); } @Override public Loader<Cursor> onCreateLoader(int id, Bundle extras) { Uri uri = CustomContract.Comments.CONTENT_URI; return new CursorLoader(mActivity, uri, null, null, null, null); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { mAdapter.swapCursor(cursor); } @Override public void onLoaderReset(Loader<Cursor> loader) { mAdapter.swapCursor(null); } protected class CommentsObserver extends ContentObserver { public CommentsObserver() { super(new Handler()); } @Override public void onChange(boolean selfChange) { super.onChange(selfChange); // TODO Trigger a reload. } } } ``` In the associated ContentProvider I added `notifyChange()` for the insert action. ``` @Override public Uri insert(Uri uri, ContentValues values) { final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int match = sUriMatcher.match(uri); switch (match) { case COMMENTS: { long id = db.insert(DatabaseProperties.TABLE_NAME_COMMENTS, null, values); Uri itemUri = ContentUris.withAppendedId(uri, id); // FIXME Which one is right? getContext().getContentResolver().notifyChange(itemUri, null); getContext().getContentResolver().notifyChange(uri, null); return itemUri; } default: { throw new UnsupportedOperationException("Unknown URI: " + uri); } } } ``` Questions: - Is it okay to pass `null` to `notifyChange()` as the observer parameter? If not, what object am I supposed to pass here? - Should I pass the `uri` or the `itemUri` in `notifyChange()`? Why? - What method do I have to call in `CommentsObserver#onChange()` to update the list of comments? - Is there no interface I could implement with the `ListFragment` instead of the inner class instance of `ContentObserver`? - I simply instantiate a `new Handler()` in the constructor of `CommentsObserver`. This seems not correct to me - please explain.

Original source