Question

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:

  1. Query SQLite for cached data. cursor.setNotificationUri(getContext().getContentResolver(), uri) is called on the returned cursor.

  2. Start an asynchronous call to remote API. Once the response is received, getContext().getContentResolver().notifyChange(uri, null) is called.

  3. Sleep for 5 seconds to cause a delay in receipt of cursor by the CursorLoader. (This is done to easily reproduce my problem).

  4. 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.

Était-ce utile?

La solution

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);

Autres conseils

get a reference to your loader while initializing as follows

Loader dayWeatherLoader = getLoaderManager().initLoader(LOADER_DAY_WEATHER, null, this);

then create a class that extends ContentObserver as follows

class DataObserver extends ContentObserver {

public DataObserver(Handler handler) {
    super(handler);
}

@Override
public void onChange(boolean selfChange, Uri uri) {
    dayWeatherLoader.forceLoad();
}
}

Then register content observer inside onResume lifecycle method as follows

@Override
public void onResume() {
super.onResume();
getContext().getContentResolver().registerContentObserver(CONTENTPROVIDERURI,true,new DayWeatherDataObserver(new Handler()));
}

Whenever there is a change in the underlying data of content provider, the onChange method of contentobserver will be called where you can ask loader to load the data again

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top