Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ContentProvider calls atomic? Save on onPause, load in OnActivityCreated, old data

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?

like image 395
Pork 'n' Bunny Avatar asked Nov 12 '22 04:11

Pork 'n' Bunny


1 Answers

It seems like an obvious race condition that the save to the content provider is not completed in the onPause before it is loaded after the rotate.

By assuming above statement is correct:

Usually content provider is fast and it is not suppose to take this much time. But, When ContetnProvider backed by SQLite, it is understandable that it will take time to insert data into database. Where as delay time will depend on amount of data and device hardware.

But, the problem with this approach is when, user just rotate the screen and really do not like to wait, to see data are being loaded one by one. Rotation should be fast very fast.

Alternative on this path is,

  • your wait for your onPause to complete the task(this will block the UI being created next time and very very bad idea)
  • You set manifest tag which take care of rotation.

Further alternative on this path is, create a in-memory cache only for handling rotation case. That means every time you look for data first you try to find it inside cache, if not, try to load from ContentProvider. You can use the same Content URI for the key of the cache. And, for writing, write first on the cache then in provider.

ContentProvider provide some great benefits, no doubt on that. If you need ContentProvideryou should use it. But, for handling rotation you can think of other alternative. And, Way around solution would be, dumping things on a file and read that back. SharedPreferences or typical File IO right now on top of my head. I guess their is a good reason these exist.

Finally, just out of curiosity, have you tried by loading data onResume state of the fragment? And, hopefully setRetainInstance(boolean) is not set, somewhere in your code.

Edit,

AFAIK, ContentProvider do not provider any atomicity or thread safety. From Android doc,

the methods query(), insert(), delete(), update(), and getType()—are called 
from a pool of threads in the content provider's process, not the UI thread 
for the process. Because these methods might be called from any number of 
threads at the same time, they too must be implemented to be thread-safe. 

Please read this and this

Probably i will be able to give you a better answer if i can see your code.

like image 111
minhaz Avatar answered Nov 15 '22 13:11

minhaz