12

我已经多次使用共享首选项,但由于某种原因,更改没有保存在我正在测试的新应用程序中。这是重要代码的片段:

SharedPreferences sp = getSharedPreferences(getString(R.string.key_preferences), MODE_PRIVATE);
Set<String> widgets = sp.getStringSet(getString(R.string.key_widgets), (new HashSet<String>()));
widgets.add(name + " " + Integer.toString(appWidgetId) + " " + address);
sp.edit().putStringSet(getString(R.string.key_widgets), widgets).commit();

我使用日志记录来检查小部件是否已添加到集合中,但从未保存更新的集合。如果我将最后一行更改为...

sp.edit().putStringSet(getString(R.string.key_widgets), widgets).putString("testkey", "testvalue").commit();

...然后一切都保存得很好。我错过了什么?

*更新:

我发现这也有效:

SharedPreferences sp = getSharedPreferences(getString(R.string.key_preferences), MODE_PRIVATE);
Set<String> widgets = sp.getStringSet(getString(R.string.key_widgets), (new HashSet<String>()));
Set<String> newWidgets = new HashSet<String>();
for (String widget : widgets) newWidgets.add(widget);
newWidgets.add(name + " " + Integer.toString(appWidgetId) + " " + address);
sp.edit().putStringSet(getString(R.string.key_widgets), newWidgets).commit();

也许我错过了文档中关于需要为编辑器创建一个新对象来保存首选项的内容。

*更新2:

如果我创建一个编辑器对象没有区别:

SharePreferences.Editor spe = sp.edit();
spe.putStringSet(getString(R.string.key_widgets), widgets)
spe.commit();
4

6 回答 6

17

我们只需要更仔细地阅读文档

根据getStringSet

请注意,您不得修改此调用返回的集合实例。如果您这样做,则无法保证存储数据的一致性,您也无法修改实例。

事实上,应该在SharedPreferences.Editor中注明不要向 putStringSet 发送一个以后可能会修改的集合。在修改之前复制从getStringSet返回的集合,并在将集合发送到 putStringSet 之前复制它。

SharedPreferences myPrefs = getSharedPreferences(myPrefName, MODE_PRIVATE);
HashSet<String> mySet = new HashSet<string>(myPrefs.getStringSet(mySetKey, new HashSet<string()));
....
SharedPreferences.Editor myEditor = myPrefs.edit();

然后其中之一

myEditor.putStringSet(mySetKey, new HashSet<String>(mySet));

或者

myEditor.putStringSet(mySetKey, (Set<String>) mySet.clone());
于 2013-12-29T20:59:30.897 回答
10

我想稍微改进一下Chike的答案。

原因

如果我们看一下实现(自 v4.0.1 以来未更改),commit()没有保存到磁盘的原因是显而易见的,它首先提交到内存,然后等待磁盘写入完成:

public boolean commit() {
    MemoryCommitResult mcr = commitToMemory();
    SharedPreferencesImpl.this.enqueueDiskWrite(
        mcr, null /* sync write on this thread okay */);
    try {
        mcr.writtenToDiskLatch.await();
    } catch (InterruptedException e) {
        return false;
    }
    notifyListeners(mcr);
    return mcr.writeToDiskResult;
}

关键是,如果我们使用来自getStringSet()的相同实例调用putStringSet() ,则不会检测到任何变化:

// Returns true if any changes were made
private MemoryCommitResult commitToMemory() {
    MemoryCommitResult mcr = new MemoryCommitResult();
    ...
            for (Map.Entry<String, Object> e : mModified.entrySet()) {
                String k = e.getKey();
                Object v = e.getValue();
                ...
                    if (mMap.containsKey(k)) {
                        Object existingValue = mMap.get(k);
                        if (existingValue != null && existingValue.equals(v)) {
                            continue;
                        }
                    }
                    mMap.put(k, v);
                }

                mcr.changesMade = true;
                ...
            }
            ...
        }
    }
    return mcr;
}

由于没有进行任何更改,因此 writeToFile()很乐意跳过磁盘写入并设置成功标志:

private void writeToFile(MemoryCommitResult mcr) {
    // Rename the current file so it may be used as a backup during the next read
    if (mFile.exists()) {
        if (!mcr.changesMade) {
            // If the file already exists, but no changes were
            // made to the underlying map, it's wasteful to
            // re-write the file.  Return as if we wrote it
            // out.
            mcr.setDiskWriteResult(true);
            return;
        }
        ...
    }
    ...
}

解决方案

请参考Chike的回答,复制 String 集,然后再将其保存到相同的 SharedPreference 键。

于 2015-09-08T11:42:27.357 回答
1

您需要保存 Editor 对象,然后调用 commit()(Android 2.3 之前)或 apply()(Android 2.3 及更高版本)。

SharedPreferences.Editor editor = sp.edit();
editor.put...
editor.commit();
于 2012-05-23T12:41:18.180 回答
1

我遇到了你描述的同样的问题。

我的布尔值和字符串首选项存储没有问题,但是当我尝试存储字符串集时,问题就开始了。

当我将第一个元素添加到字符串集中并存储它时,它就起作用了。但是,当我尝试添加第二个时,它会添加到内存中(我可以刷新一个视图,显示存储在该首选项中的所有元素)。如果我强制关闭我的应用程序并重新启动它,共享首选项只会添加第一个元素。

我调试了我的代码,发现当我添加一个新的 HashSet 时,我存储字符串的 HashSet 没问题,但是更改没有写入持久存储

于 2012-12-17T10:27:55.643 回答
0

对 Shared Preferences 使用以下结构:

1.创建一个对象

SharedPreferences myPrefs = context.getSharedPreferences(PREF_NAME, MODE_WORLD_READABLE);

2. 在共享首选项中保存值:

为 Shared Preference 创建一个编辑器:

SharedPreferences.Editor prefsEditor = myPrefs .edit();

然后将值放入 Editor :

prefsEditor.putString("KEY", VALUE);

现在提交更改以保存 Shared Preference :

prefsEditor.commit();
于 2012-05-23T13:13:16.507 回答
0

我将下面的代码放在 onCreate 方法中,所以每次我重新启动应用程序时它都会重置。

    preferences.edit().putInt(...).apply();

请改用此处所述的方法:https ://stackoverflow.com/a/28789934/11266070

    SharedPreferences sharedPrefs = getSharedPreferences("sp_name", MODE_PRIVATE);
    SharedPreferences.Editor ed;
    if(!sharedPrefs.contains("initialized")){
        ed = sharedPrefs.edit();

        //Indicate that the default shared prefs have been set
        ed.putBoolean("initialized", true);

        //Set some default shared pref
        ed.putString("myDefString", "wowsaBowsa");

        ed.commit();
    }  

希望这会提醒某人...

于 2020-01-03T08:21:47.580 回答