Android SharedPreferences没有保存

piu*_*lte 11 android sharedpreferences

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

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

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

sp.edit().putStringSet(getString(R.string.key_widgets), widgets).putString("testkey", "testvalue").commit();
Run Code Online (Sandbox Code Playgroud)

...然后一切都很好.我错过了什么?

*更新:

我发现这也有效:

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

也许我在文档中遗漏了一些关于需要为编辑器创建新对象以保存prefs的内容.

*更新2:

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

SharePreferences.Editor spe = sp.edit();
spe.putStringSet(getString(R.string.key_widgets), widgets)
spe.commit();
Run Code Online (Sandbox Code Playgroud)

小智 14

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

根据getStringSet

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

事实上,它应该已经注意到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();
Run Code Online (Sandbox Code Playgroud)

其中之一

myEditor.putStringSet(mySetKey, new HashSet<String>(mySet));
Run Code Online (Sandbox Code Playgroud)

要么

myEditor.putStringSet(mySetKey, (Set<String>) mySet.clone());
Run Code Online (Sandbox Code Playgroud)


Rog*_*ang 8

我想改善一下Chike的答案.

原因

之所以提交()没有保存到磁盘是显而易见的,如果我们看一看的实施(因为V4.0.1不变),其承诺的内存,然后再等待磁盘写入完成:

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

关键是,如果我们使用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;
}
Run Code Online (Sandbox Code Playgroud)

由于没有进行任何更改,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;
        }
        ...
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

请参阅Chike的答案,在将其保存到相同的SharedPreference键之前复制String set.