【问题标题】:Android SharedPreferences String Set - some items are removed after app restartAndroid SharedPreferences String Set - 一些项目在应用重启后被移除
【发布时间】:2013-11-25 18:47:16
【问题描述】:

我在共享首选项中保存了一个字符串集,如果我读出它就可以了。我开始其他活动,回去再读一遍,就可以了。如果我关闭应用程序并重新启动它,我会得到集合,但只有 1 项而不是 4 项。它一直都在发生。有已知问题吗?我会做错什么?

在一个类中,在应用程序的 oncreate 方法中创建的内容有一个 SharedPreferences 和一个 SharePreferences.Editor 变量。我在保存和加载方法中使用它们。

public void saveFeedback(FeedbackItem feedbackItem) {
    checkSp();
    Set<String> feedbackSet = getFeedbacksSet();
    if(feedbackSet == null){
        feedbackSet = new HashSet<String>();
    }
    JSONObject json = createJSONObjectfromFeedback(feedbackItem);
    feedbackSet.add(json.toString());
    ed.putStringSet(CoreSetup.KEY_FEEDBACK, feedbackSet);
    ed.commit();
}

public Set<String> getFeedbacksSet(){
    checkSp();
    Set<String> ret = sp.getStringSet(CoreSetup.KEY_FEEDBACK, null);
    return ret;
}

private void checkSp(){
    if(this.sp == null)
        this.sp = applicationContext.getSharedPreferences(applicationContext.getPackageName(), Context.MODE_PRIVATE);
    if(this.ed == null)
        this.ed = this.sp.edit();
}

我只是不明白这是怎么发生的,在应用程序运行时完美地存储所有项目,然后在重新启动后并非所有项目都在集合中。而且我认为,如果所有项目都被移除,那么它可能比一些项目消失更容易接受,而一个项目仍然存在。有解释吗?

【问题讨论】:

  • 值在哪里存储?
  • 我在代码中没有看到任何地方向 feedbackSet 添加了多个项目。因此,根据您的代码,返回的集合仅包含单个集合。
  • @MadhurAhuja 请再次阅读代码。在 saveFeedback 方法中,我添加了一个要设置的反馈项。在我的测试中,我写了 4 个反馈(写 1 个反馈,添加到集合... x4)所以如果您调用 saveFeedback 方法 4 次,它会在集合中添加 4 个反馈项...
  • @Gunaseelan 在 SharedPreferences 中...关键是 CoreSetup.KEY_FEEDBACK,类型是 StringSet。或者你的问题是什么?
  • 那是错误的。它不会将 4 个反馈项添加到集合中。您需要先添加 4 个项目,然后再提交。否则,您最后的反馈将被最新的书面反馈覆盖。您需要将提交移出该函数并在将 4 个反馈添加到集合后单独调用它。

标签: android sharedpreferences


【解决方案1】:

根据您的问题,您应该仅在将 4 个项目添加到集合后才调用 commit。 在您的代码中,您为每个反馈调用 commit,这将覆盖以前的反馈。

更新http://developer.android.com/reference/android/content/SharedPreferences.html#getStringSet(java.lang.String, java.util.Set)

Note that you must not modify the set instance returned by this call. The consistency of the stored data is not guaranteed if you do, nor is your ability to modify the instance at all.

这正是你正在做的事情

【讨论】:

  • 请再次阅读代码。该方法执行以下操作: - 从 Preferences 获取 StringSet - 向其中添加项目 - 如果将 StringSet 保存到 Preferences。我调用了 4 次它应该向它添加 4 个项目
  • 感谢您的指点!像这样包装它解决了这个问题: new HashSet(mPrefs.getStringSet(KEY, new HashSet()));
  • 这种奇怪的行为......将项目复制到一个新的集合就可以了
【解决方案2】:

这个“问题”记录在SharedPreferences.getStringSet()

getStringSet() 返回 SharedPreferences 中存储的 HashSet 对象的引用。当您向该对象添加元素时,它们实际上是在内部 SharedPreferences 中添加的。

解决方法是复制返回的 Set 并将新 Set 放入 SharedPreferences。我测试了它,它可以工作。

在 Kotlin 中,就是

    val setFromSharedPreferences = sharedPreferences.getStringSet("key", mutableSetOf())
    val copyOfSet = setFromSharedPreferences.toMutableSet()
    copyOfSet.add(addedString)

    val editor = sharedPreferences.edit()
    editor.putStringSet("key", copyOfSet)
    editor.apply() // or commit() if really needed

【讨论】:

  • 当你向这个对象添加元素时,它们实际上是在 SharedPreferences 中添加的。 所以.. 为了向 SharPref 中的Set&lt;&gt; 添加元素,我没有必须apply()他们?
  • 快速浏览一下 android 源代码就会发现这一点。当您 putStringSet() 时,它会复制该副本并将其标记为已修改。但是当你 getStringSet() 并且只是修改它时,它不会被标记为已修改,所以 apply 或 commit 不会将它写入磁盘。因此,当您的应用重新启动时,数据不会反映您的更改。 (而且我猜数据更改的回调也不会被调用)
【解决方案3】:

尝试创建您的集合的副本,然后您可以将其保存在相同的首选项中:

private Set<String> _setFromPrefs;


public void GetSetFromPrefs()
{
    SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getContext());
    Set<String> someSets = sharedPref.getStringSet("some_sets", new HashSet<String>() );
    _setFromPrefs = new HashSet<>(someSets); // THIS LINE CREATE A COPY
}


public void SaveSetsInPrefs()
{
    SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getContext());
    SharedPreferences.Editor editor = sharedPref.edit();
    editor.putStringSet("some_sets", _setFromPrefs);
    editor.commit();
}

【讨论】:

    【解决方案4】:

    遇到了同样的问题。通过在实例化之后和提交之前清除编辑器来解决它。

    sPrefs = PreferenceManager.getDefaultSharedPreferences(context);
        sFavList = sPrefs.getStringSet(context.getResources().getString(R.string.pref_fav_key), null);
        sEditor = sPrefs.edit();
        sEditor.clear(); // added clear, now Set data persists as expected
        if (sFavList == null) sFavList = new HashSet<>();
        sFavList.add(title);
        sEditor.putStringSet(context.getResources().getString(R.string.pref_fav_key), sFavList).apply();
    

    我尝试按照其他人的建议创建副本,但这对我不起作用。

    找到了这个解决方案here

    【讨论】:

      【解决方案5】:

      根据文档,SharedPreferences 中的 String Set 应该被视为不可变的,当试图修改它时,事情就变糟了。解决方法是: 获取现有集,制作副本,更新副本,然后将其保存回共享首选项,就像它是新集一样。

      Set<String> feedbackSet = getFeedbacksSet();
      if(feedbackSet == null){
          feedbackSet = new HashSet<String>();
      }
      
      //make a copy of the set, update the copy and save the copy
      Set<String> newFeedbackSet = new HashSet<String>();
      JSONObject json = createJSONObjectfromFeedback(feedbackItem);
      newFeedbackSet.add(json.toString());
      newFeedbackSet.addAll(feedbackSet);
      ed.putStringSet(CoreSetup.KEY_FEEDBACK, newFeedbackSet);
      ed.commit();
      

      【讨论】:

        【解决方案6】:
        public void saveFeedback(FeedbackItem feedbackItem) {
            checkSp();
            Set<String> feedbackSet = getFeedbacksSet();
            if(feedbackSet == null){
                feedbackSet = new HashSet<String>();
            }
            JSONObject json = createJSONObjectfromFeedback(feedbackItem);
            feedbackSet.add(json.toString());
            ed.putStringSet(CoreSetup.KEY_FEEDBACK, null);
            ed.commit();
            ed.putStringSet(CoreSetup.KEY_FEEDBACK, feedbackSet);
            ed.commit();
        }
        

        【讨论】:

          【解决方案7】:

          将字符串保存在共享首选项中

             SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit();
             editor.putString("text", mSaved.getText().toString());
             editor.commit();
          

          从共享偏好中检索数据

           SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
           String restoredText = prefs.getString("text", null);
          

          【讨论】:

          • 但是我有一个 StringSet 而不仅仅是一个 String。
          • 这意味着你有数组列表然后做一件事使用for循环并将字符串从第一个索引发送到最后一个索引..
          • ?我应该如何将集合或列表存储在字符串中?但无论如何,问题解决了,错误与您的答案无关。不过谢谢你的评论。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-02-03
          • 2017-04-20
          • 1970-01-01
          • 2021-11-18
          • 1970-01-01
          • 2014-06-22
          相关资源
          最近更新 更多