I have a fragment that has a listview.
In onPause() I'm saving off the Y scroll position of the listview in a ContentProvider.
The same fragment upon onResume or onActivityCreated uses a loader to grab that y scroll position from the contentprovider and restore the scroll position.
If I exit the activity/fragment and return to it, this works, the listview returns to its last opened location since it was saved off to the contentprovider in onPause. So the code is 100% fine.
What isn't fine, is the data on a rotate. onPause saves fine, but the load after onCreateActivity results in retrieving old data, the data prior to the save in onPause. It results in the listview returning to the OLD position when they first opened the app and not the position where the listview was prior to the rotation.
It seems like an obvious race condition that the save to the content provider during onPause is not completed in the onPause, resulting in old data being loaded after the rotate.
So a rotate on my phone looks like this
01:31:33.026: ThreadViewerFragment.java(235):onPause Saved(position:Yposition): 59:-74
01:31:33.256: ThreadViewerFragment.java(194):onActivityCreated
01:31:33.266: ThreadViewerFragment.java(309):onLoadFinished Load(position:Yposition): 62:-149 //initial load is of old values
01:31:33.266: ThreadViewerFragment.java(309):onLoadFinished Load(position:Yposition): 62:-149
01:31:33.596: ThreadViewerFragment.java(309):onLoadFinished Load(position:Yposition): 59:-74 //this is loaded due to notification by the save in onPause which is supposed to be finished before recreating the new fragment???
So the order looks fine (doesn't look like a race condition in terms of activity/fragment flow) but clearly, it's loaded old values instead of the just saved values of 59:-74.
I'm not after a work around, I know how to use saveInstanceState etc. But why should I double up my code, is there a way to force the contentprovider to behave atomically (which I thought it already was?)
Edit: adding code and refined the question a bit better because I'm still not satisfied that we are any closer to understanding if contentprovider calls block while being executed and/or if those calls are atomic or if its just a misunderstanding of contentproviders and loaders.
In onPause, I'm saving off the Y position of the product listing
@Override
public void onPause() {
super.onPause();
Utils.logv("onPause Saved position: " + mLastRead + ", " + mYPos);
ContentValues contentValues = new ContentValues();
contentValues.put(ProductsContract.Products.Y_POS, mYpos);
int updateCount = getActivity().getContentResolver().update(
Uri.parse(ProductsContract.Products.CONTENT_URI + "/" + mId),
contentValues, null, null);
}
My understand is that the call to update should be a blocking call, and it occurs before the fragment is destroyed and before the new fragment is created to handle the rotation
In on resume I fire up my loader
public void onResume() {
super.onResume();
getLoaderManager().initLoader(PRODUCTS_LOADER_ID, null, this);
}
And in my loader I get the cursor, which reliably has old data after a rotate, but is fine in any other circumstance
@Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
if(cursor.moveToFirst()){
mYpos = cursor.getInt(cursor.getColumnIndex(ProductsContract.Products.Y_POS));
Utils.logv("loader: " + mYpos);
}
}
So restating, after a rotate, the loader will consistently deliver old data.
I'm thinking perhaps it's the loader that is stale and not the content provider itself? That the cursor is saved and restored after the rotate even though it's stale?