I have a content provider, a content resolver, and a cursor loader. The loader is used to indirectly populate a listview (ie not a simple cursor adapter, rather an array adapter, since I need to use the cursor's results to gather other data).
When I change the underlying data, the listview does not re-populate as the onLoadFinished(Loader<Cursor>, Cursor)
call back is not called.
As suggested while I'm writing this, there are a lot of questions on this issue. eg CursorLoader not updating after data change
And all the questions point out two things:
- In your content provider query() method, you need to tell the cursor about notifications a'la
c.setNotificationUri(getContext().getContentResolver(), uri);
- In your content provider insert/update/delete method, you need to notify on the URI:
getContext().getContentResolver().notifyChange(uri, null);
I'm doing those things.
I'm also NOT closing any of the cursors I get back.
Note, my content provider lives in a separate app (think of it as a content provider app -- no launcher main activity). A com.example.provider APK, and the com.example.app is calling (in the content resolver) via the content://com.example.provider/table URI etc. The content resolver (dbhelper) lives in a library project (com.example.appdb) that the activity links in. (This way, multiple projects can use the dbhelper via linking, and all content providers are installed via single APK)
I have turned on debugging in the loader manager, and can see where I force a refresh after the data changes (ie the loader being restarted and previous being marked inactive), but nothing that says anything is happening automatically -- rather, in response to my force refresh.
Any ideas why my loader isn't being refreshed ?
-- EDIT --
The loader create:
Log.d(TAG, "onCreateLoader ID: " + loaderID);
// typically a switch on the loader ID, and then
return dbHelper.getfoo()
Where getfoo() returns a cursor loader via:
return new CursorLoader(context, FOO_URI, foo_Fields, foo_query, foo_argArray, foo_sort );
Loader Finish takes the cursor and populates a table (and does some processing) -- nothing fancy there.
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
Log.d(TAG, "onLoadFinished id: " + loader.getId());
// switch on loader ID ..
FillTable(cursor, (FooAdapter) foo_info.listview.getAdapter();
Loader Reset clears the table.
public void onLoaderReset(Loader<Cursor> loader) {
Log.d(TAG, "onLoaderReset id: " + loader.getId());
// normally a switch on loader ID
((FooAdapter)foo_info.listview.getAdapter()).clear();
The content provider does:
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Cursor cursor;
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
SQLiteDatabase db = dbHelper.getReadableDatabase();
switch (sUriMatcher.match(uri)) {
case FOO:
qb.setTables(FOO);
qb.setProjectionMap(FooProjectionMap);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
Then insert/update is similar:
public Uri insert(Uri uri, ContentValues initialValues) {
ContentValues values;
if (initialValues != null) {
values = new ContentValues(initialValues);
} else {
values = new ContentValues();
}
String tableName;
Uri contentUri;
switch (sUriMatcher.match(uri)) {
case FOO:
tableName = FOOTABLE;
contentUri = FOO_URI;
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
SQLiteDatabase db = dbHelper.getWritableDatabase();
long rowId = db.insert(tableName, null, values);
if (rowId > 0) {
Uri objUri = ContentUris.withAppendedId(contentUri, rowId);
getContext().getContentResolver().notifyChange(objUri, null);
return objUri;
}
throw new SQLException("Failed to insert row into " + uri);
}