ContentProvider调用原子?保存onPause,加载OnActivityCreated旧数据

Por*_*nny 5 sqlite android android-contentprovider android-fragments

我有一个包含listview的片段.

在onPause()中,我将在ContentProvider中保存listview的Y滚动位置.

onResume或onActivityCreated上的相同片段使用加载器从contentprovider获取y滚动位置并恢复滚动位置.

如果我退出活动/片段并返回它,这是有效的,listview返回到它最后打开的位置,因为它被保存到onPause中的contentprovider.所以代码是100%罚款.

什么不好,是旋转数据.onPause保存得很好,但onCreateActivity之后的加载导致检索旧数据,即onPause中保存之前的数据.它导致listview在第一次打开应用程序时返回到OLD位置,而不是listview在旋转之前的位置.

似乎是一个明显的竞争条件,在onPause期间保存到内容提供程序未在onPause中完成,导致在旋转后加载旧数据.

所以在我的手机上旋转看起来像这样

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???
Run Code Online (Sandbox Code Playgroud)

因此,顺序看起来很好(在活动/片段流方面看起来不像竞争条件)但显然,它加载的是旧值而不是刚刚保存的值59:-74.

我不是在解决方法之后,我知道如何使用saveInstanceState等.但是为什么我应该加倍我的代码,是否有办法强制contentprovider以原子方式运行(我认为它已经是?)

编辑:添加代码并更好地改进问题,因为我仍然不满意我们是否更接近理解内容提供者在执行时调用阻塞和/或这些调用是原子的还是只是对内容提供者和加载器的误解.

在onPause中,我正在保存产品列表的Y位置

@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);
}
Run Code Online (Sandbox Code Playgroud)

我的理解是对update的调用应该是一个阻塞调用,它发生在片段被销毁之前和创建新片段以处理旋转之前

在简历中,我启动了我的装载机

public void onResume() {
    super.onResume();

    getLoaderManager().initLoader(PRODUCTS_LOADER_ID, null, this);
}
Run Code Online (Sandbox Code Playgroud)

在我的加载器中,我得到了光标,它在旋转后可靠地具有旧数据,但在任何其他情况下都可以

 @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);
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,重新启动后,加载器将始终如一地提供旧数据.

我想也许它是陈旧的加载器而不是内容提供商本身?光标在旋转后保存并恢复,即使它已经过时了?

min*_*haz 1

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.\n
Run Code Online (Sandbox Code Playgroud)\n\n

假设上述陈述是正确的:

\n\n

通常内容提供商速度很快,不会花费这么多时间。但是,在 的ContetnProvider支持下SQLite,将数据插入数据库需要时间是可以理解的。延迟时间取决于数据量和设备硬件。

\n\n

但是,这种方法的问题是,用户只需旋转屏幕,并且真的不喜欢等待,看看数据正在被一一加载。Rotation should be fast very fast.

\n\n

这条道路上的替代方案是,

\n\n
    \n
  • 您等待onPause完成任务(这将阻止下次创建 UI,这是非常非常糟糕的主意)
  • \n
  • 您设置负责轮换的清单标记。
  • \n
\n\n

此路径上的进一步替代方案是,创建仅用于处理旋转情况的内存缓存。这意味着每次您查找数据时,您都会首先尝试在缓存中找到它,如果没有,请尝试从ContentProvider. 您可以使用相同的 Content URI 作为缓存的键。并且,对于写入,首先写入缓存,然后写入提供程序。

\n\n

ContentProvider毫无疑问,它提供了一些巨大的好处。如果你需要ContentProvider你应该使用它。但是,为了处理旋转,您可以考虑其他选择。并且,解决方案是将内容转储到文件中并读回。SharedPreferences或者典型的文件 IO 现在就在我的脑海中。我想这是它们存在的一个很好的理由。

\n\n

最后,出于好奇,您是否尝试过加载onResume片段的数据状态?并且,希望setRetainInstance(boolean)在您的代码中的某个位置没有设置。

\n\n

编辑

\n\n

AFAIK,ContentProvider 不提供任何原子性或线程安全性。来自 Android 文档,

\n\n
the methods query(), insert(), delete(), update(), and getType()\xe2\x80\x94are called \nfrom a pool of threads in the content provider\'s process, not the UI thread \nfor the process. Because these methods might be called from any number of \nthreads at the same time, they too must be implemented to be thread-safe. \n
Run Code Online (Sandbox Code Playgroud)\n\n

请阅读这个这个

\n\n

如果我能看到您的代码,也许我能够给您更好的答案。

\n