I have a CursorLoader
that loads data from local SQLite
database.
The data in SQLite
is actually a cached copy of a remote API
response, so I start an asynchronous refresh of SQLite
data whenever it is queried by the CursorLoader
.
Following is the sequence of steps I follow to respond to the data query in my ContentProvider
:
Query SQLite
for cached data. cursor.setNotificationUri(getContext().getContentResolver(), uri)
is called on the returned cursor.
Start an asynchronous call to remote API
. Once the response is received, getContext().getContentResolver().notifyChange(uri, null)
is called.
Sleep for 5
seconds to cause a delay in receipt of cursor by the CursorLoader
. (This is done to easily reproduce my problem).
Return the cursor obtained in Step 1
to the CursorLoader
.
In the less likely but possible scenario of the asynchronous remote API
call completing (and notifyChange()
being called) before the CursorLoader
could register a ContentObserver
on the obtained cursor by calling registerContentObserver(cursor, mObserver)
[ see http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/content/CursorLoader.java#CursorLoader.loadInBackground%28%29 ], the data change notification is missed by the CursorLoader
and therefore the Activity
does not see updated data.
I can get around this problem by writing my LoaderCallBacks
such that the data is loaded twice - first only cached data is loaded and then a refresh
request is made. This way the CursorLoader
gets to register ContentObserver
before the refresh
call starts.
However, this is something that should ideally be handled by the CursorLoader
itself, as CursorLoader
advertises automatic update of the cursor. Doing it in every Activity
causes a lot of bloat in Activity
code.
My approach is to register the observer before starting with the loading of the cursor. You need to register for the Uri instead of for the cursor.
getActivity().getContentResolver().registerContentObserver(
your_Uri, true,
new YourObserver(mHandler));
getLoaderManager().initLoader(0, null, this);