【问题标题】:Android - shared preferences set in an AsyncTask onPostExecute() not always set?Android - 在 AsyncTask onPostExecute() 中设置的共享首选项并不总是设置?
【发布时间】:2012-09-18 05:41:59
【问题描述】:

我有一些代码在 98% 的时间内都可以正常工作,并且在我自己的测试期间 100% 可以正常工作,因此除了让用户设备遇到此问题之外,我无法真正重现该问题。

我在 onPostExecute() 中所做的是设置这样的参数:

   SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences( AddProblemActivity.this);
            prefs.edit().putString("recent_problem_id", result ).commit();

然后进入下一个活动:

            Intent myIntent = new Intent(AddProblemActivity.this, ProblemActivity.class);
            AddProblemActivity.this.startActivity(myIntent);

然后尝试像这样在那里获取该参数:

SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences( 
              ProblemActivity.this);         

    // display a loading message before problem loads.
    String recent_problem_id = prefs.getString( "recent_problem_id" , null );

    if ( recent_problem_id == null )
    {
        // Sometimes it is null!            
    }

有人知道为什么会这样吗?

谢谢!

【问题讨论】:

  • 发生这种情况的设备——它们有什么共同点吗?比如制造商、API 版本,什么?
  • @Fildor 我自己无法在任何设备上重现这一点,实际上我不确定是否可以从活动中的代码中获取设备信息..对我来说是可能的在发生此类问题时检测有关其设备的信息?
  • 有些库可以为您提供该功能,是的。但无论如何:一定有人告诉过你,他们有这个问题?
  • 等一下……我当然知道:android.os.Build 在那里你可以得到用户正在使用的版本,硬件等等developer.android.com/reference/android/os/Build.html
  • 没有解决您的问题,但您是否使用 SharedPreferences 只是为了将 id 发送到下一个活动?你不能在意图中将它作为额外的发送吗?

标签: android


【解决方案1】:

如果您尝试将数据传递给新活动,为什么不将其作为额外的字符串放在 Intent 中?然后,从新活动的意图中获取该字符串。如果您仍然需要存储它,您可以在新 Activity 的 onCreate() 将其从 Intent 中拉出后进行存储。

Intent myIntent = new Intent(AddProblemActivity.this, ProblemActivity.class);
//Add results here
myIntent.putExtra("RecentProblemId", result);
AddProblemActivity.this.startActivity(myIntent);

然后,在新 Activity 的 onCreate 中,执行:

String recentProblemId = getIntent().getStringExtra("RecentProblemId");

现在,如果您仍需要存储此信息,请执行以下操作:

if(recentProblemId != null){
    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences( 
              ProblemActivity.this);         
    prefs.putString("recent_problem_id",recentProblemId).commit();
}

我知道这并不能完全回答您关于为什么字符串并不总是致力于 onPostExecute() 中的首选项的问题。但是,在活动之间传递信息的最佳实践是通过 Intents 和 extras。

我猜测为什么它可能并不总是对某些用户有效,是他们的设备在新 Activity 启动并尝试从同一个文件读取之前没有完成将数据写入共享首选项文件。希望这会有所帮助。

【讨论】:

  • 谢谢。是否可以对 sharedPreferences 执行 .commit(),然后下一行执行 Intent 切换,但数据仍未完成写入?有没有办法等到数据写入完成?
  • 没问题。我认为你刚才描述的可能是问题的根源。我不熟悉确保已完成写入首选项文件的方法。你有什么理由不能走我建议的 Intent 额外路线吗?
【解决方案2】:

首先,请参阅 Raghav Sood 的回答。
有一个微妙的时刻。您可能会开始执行 AsyncTask 而不是旋转设备。 Activity 将被重新创建,并且在 PostExecute 中您将有错误的上下文,因此您的首选项将不会被保存。
如果是真的,你应该使用 onRetainNonConfigurationInstance() 来保存适当的任务实例。

【讨论】:

    【解决方案3】:

    我不确定这一点,但我认为问题可能是由于您传递的上下文不同。您首先使用AddProblemActivity 的上下文,然后是ProblemActivity 的上下文。尝试使用一组首选项,例如文件名:

    SharedPreferences prefs = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE);
    

    请注意,getSharedPreferences() 是来自 Context 的方法,因此您需要在 AsyncTask 中引用 Activity 或 Application Context 才能使用它。

    【讨论】:

    • 有趣。我会考虑的。
    • 但是如果“错误的上下文”是问题的答案;这不是意味着应用程序的所有实例都会失败 - 一直(100% 失败)吗?
    • 这可能有效,但我认为以这种方式将数据传递给新活动并不是最好的主意。使用 Intents and extras 查看我的答案。这会在每次启动新 Activity 时导致读/写。 Intent extra 不是这种情况。
    • 对,所有这些事情只有不到 5% 的时间失败。所以大多数时候它们出于某种原因正常工作。
    • 由于@Genadinik 正在使用 getDefaultSharedPreferences() 并且两个活动都属于同一个应用程序,我认为这不是问题所在。他可以尝试使用在 onPostExecute() 方法结束时执行的日志输出来查看 dbm 的答案是否正确。
    【解决方案4】:

    看起来你的代码很好,没有理由不应该工作,唯一的原因是一些可能与设备相关的缺陷。我的想法是,由于共享首选项保存在本地存储中,因此在此过程中可能会出现问题。

    根据 cmets 的建议,必须添加设备类型日志, 我建议您使用“ACRA”-http://code.google.com/p/acra/,它可以轻松地为您提供详细的报告(请注意,您可以不必仅在应用程序崩溃时发送报告)。

    看看这个帖子,他展示了一个你可能也遇到的问题: SharedPreferences will not save/load in PreferenceActivity。 如果是这种情况,解决方案将是手动处理在本地存储上保存这些持久数据或使用数据库解决方案。祝你好运:)

    【讨论】:

      【解决方案5】:

      我认为问题是由于当您尝试从第二个活动中访问共享首选项设置时尚未将其写入您的文件系统。您提到您从onPostExecute 方法(可能是AsyncTask?)编写设置。当您启动AsyncTask 时,无法保证它会立即启动。唯一的保证是它将在后台线程中启动。平台可以并且将决定何时实际运行后台线程,具体取决于系统负载、文件系统块等。当您切换到第二个活动时,很可能您的AsyncTask 尚未启动(因此尚未调用onPostExecute 方法)。

      在写入文件系统时保存共享首选项很棘手。如果你不小心,你最终可能会阻塞主线程。 SharedPreferences.Editor 对象也有一个 apply 方法,它将立即更新共享首选项的内存缓存(使更改立即可用)并启动后台线程以将实际值保存到文件系统中好吧。所以我的建议是,如果你有可能,你应该尝试调用apply 方法(从你的主线程),而不是从(我假设的)AsyncTask 调用commit 方法。 apply 方法需要 API 级别 9 或更高。

      供您参考:http://developer.android.com/reference/android/content/SharedPreferences.Editor.html

      编辑:

      commit 方法将根据写入操作的结果返回一个布尔值。您可以(应该?)检查该返回值,以便至少能够对失败采取正确的应对措施(例如显示“无法保存您的设置,请重试”toast 或其他内容)。

      干杯, --dbm

      【讨论】:

      • 关于 "很可能是当您切换到第二个活动时,您的 AsyncTask 尚未启动(因此尚未调用 onPostExecute 方法) .",从问题中我得到的印象是第二个活动onPostExecute开始,设置首选项之后。不过我可能是错的。
      • @Joe:啊,这似乎是一个公平的假设。尽管如此,我认为我的陈述仍然站得住脚:不应从AsyncTasks 写入共享首选项,因为您不知道何时执行后台线程。
      【解决方案6】:

      我可能是错的。但我相信我的一个朋友曾经遇到过类似的问题。我被这个问题困了几个小时。结果在大约 30% 的时间里不起作用。我相信 onPostExecute() 在实例化 Intent 并调用活动时在单独的线程上运行。这是因为 AsyncTask 是在单独的线程上实现的。根据设备的不同,这更有可能被调用而不是被调用。我们的平板电脑很少发生,在智能手机上会更常见。

      您可以通过调试应用程序并查看 AsyncThread 线程并查看何时进行调用来测试这一点。

      是的,最好通过 putExtra() 发送变量。

      我希望这可以帮助您了解发生这种情况的原因。

      【讨论】:

        【解决方案7】:

        SharePreferences 的提交操作是同步的,所以我认为您正在启动新意图这一事实不会影响它,唯一的问题是提交操作不是故障安全的,它可能会失败。

        http://developer.android.com/reference/android/content/SharedPreferences.Editor.html#commit()

        如果新值成功写入持久化,则返回 true 存储。

        也许您应该检查该返回值以确保您设法保存了结果。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-04-08
          • 2014-10-26
          • 2022-01-08
          • 2017-07-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多