Erklärung des Quellcodes
Während die anderen guten Antworten hier richtig darauf hingewiesen haben, dass dieses potenzielle Problem in SharedPreferences.getStringSet () dokumentiert ist, möchte ich im Grunde genommen "Das zurückgegebene Set nicht ändern, da das Verhalten nicht garantiert ist" Quellcode, der dieses Problem / Verhalten für jeden verursacht, der tiefer eintauchen möchte.
Ein Blick auf SharedPreferencesImpl (Quellcode ab Android Pie) zeigt, dass SharedPreferencesImpl.commitToMemory()
ein Vergleich zwischen dem ursprünglichen Wert ( Set<String>
in unserem Fall a) und dem neu geänderten Wert erfolgt:
private MemoryCommitResult commitToMemory() {
for (Map.Entry<String, Object> e : mModified.entrySet()) {
String k = e.getKey();
Object v = e.getValue();
if (mapToWriteToDisk.containsKey(k)) {
Object existingValue = mapToWriteToDisk.get(k);
if (existingValue != null && existingValue.equals(v)) {
continue;
}
}
mapToWriteToDisk.put(k, v);
}
Wenn Sie also versuchen, Ihre Änderungen in die Datei zu schreiben, durchläuft dieser Code im Grunde genommen Ihre geänderten / hinzugefügten Schlüssel / Wert-Paare und prüft, ob sie bereits vorhanden sind, und schreibt sie nur in die Datei, wenn dies nicht der Fall ist oder ist unterscheidet sich von dem vorhandenen Wert, der in den Speicher eingelesen wurde.
Die wichtigste Zeile, auf die Sie hier achten sollten, ist if (existingValue != null && existingValue.equals(v))
. Ihr neuer Wert wird nur dann auf die Festplatte geschrieben, wenn er vorhanden existingValue
ist null
(noch nicht vorhanden ist) oder wenn sich existingValue
der Inhalt vom Inhalt des neuen Werts unterscheidet.
Dies ist der Kern des Problems. existingValue
wird aus dem Speicher gelesen. Die SharedPreferences-Datei, die Sie ändern möchten, wird in den Speicher eingelesen und als gespeichert Map<String, Object> mMap;
(später bei mapToWriteToDisk
jedem Versuch, in eine Datei zu schreiben, kopiert ). Wenn Sie anrufen, erhalten getStringSet()
Sie eine Set
von dieser In-Memory-Map zurück. Wenn Sie dann derselben Set
Instanz einen Wert hinzufügen , ändern Sie die speicherinterne Zuordnung. Wenn Sie dann aufrufen editor.putStringSet()
und versuchen, ein Commit durchzuführen, commitToMemory()
wird es ausgeführt, und die Vergleichszeile versucht, Ihren neu geänderten Wert zu vergleichen v
, existingValue
der im Grunde derselbe im Speicher ist Set
, den Sie gerade geändert haben. Die Objektinstanzen sind unterschiedlich, da die Set
s an verschiedenen Stellen kopiert wurden, der Inhalt jedoch identisch ist.
Also Sie versuchen , Ihre neue Daten zu Ihren alten Daten zu vergleichen, aber Sie haben bereits unbeabsichtigt Ihre alten Daten aktualisiert , indem Sie direkt , dass die Modifizierung Set
Instanz. Somit werden Ihre neuen Daten nicht in die Datei geschrieben.
Aber warum werden die Werte anfänglich gespeichert, verschwinden aber, nachdem die App beendet wurde?
Wie im OP angegeben, scheinen die Werte während des Testens der App gespeichert zu sein, aber die neuen Werte verschwinden, nachdem Sie den App-Prozess abgebrochen und neu gestartet haben. Dies liegt daran , während die App laufen und Sie hinzufügen Werte, du bist immer noch die Werte in die In-Memory - Zugabe Set
Struktur, und wenn Sie anrufen getStringSet()
werden Sie das gleiche im Speicher immer wieder Set
. Alle Ihre Werte sind da und es sieht so aus, als ob es funktioniert. Nachdem Sie die App beendet haben, wird diese speicherinterne Struktur zusammen mit allen neuen Werten zerstört, da sie nie in eine Datei geschrieben wurden.
Lösung
Wie andere bereits gesagt haben, vermeiden Sie es einfach, die In-Memory-Struktur zu ändern, da Sie im Grunde genommen einen Nebeneffekt verursachen. Wenn Sie also getStringSet()
den Inhalt aufrufen und als Ausgangspunkt wiederverwenden möchten, kopieren Sie ihn einfach in eine andere Set
Instanz, anstatt ihn direkt zu ändern : new HashSet<>(getPrefs().getStringSet())
. Wenn der Vergleich nun stattfindet, unterscheidet sich der In-Memory- existingValue
Wert tatsächlich von Ihrem geänderten Wert v
.