Ich versuche, eine Reihe von Zeichenfolgen mithilfe der SharedPreferences
API zu speichern .
Set<String> s = sharedPrefs.getStringSet("key", new HashSet<String>());
s.add(new_element);
SharedPreferences.Editor editor = sharedPrefs.edit();
editor.putStringSet(s);
edit.commit()
Wenn ich den obigen Code zum ersten Mal ausführe, s
wird er auf den Standardwert gesetzt (das gerade erstellte Ende leer HashSet
) und ohne Probleme gespeichert.
Beim zweiten und nächsten Ausführen dieses Codes wird ein s
Objekt mit dem ersten hinzugefügten Element zurückgegeben. Ich kann das Element hinzufügen, und während der Programmausführung wird es anscheinend im gespeichert SharedPreferences
, aber wenn das Programm beendet wird, gehen das SharedPreferences
Lesen aus seinem dauerhaften Speicher erneut und die neueren Werte gehen verloren.
Wie können die zweite und die darauf folgenden Elemente gespeichert werden, damit sie nicht verloren gehen?
android
android-preferences
JoseLSegura
quelle
quelle
Antworten:
Dieses "Problem" ist am dokumentiert
SharedPreferences.getStringSet
.Das
SharedPreferences.getStringSet
gibt eine Referenz des gespeicherten HashSet-Objekts im zurückSharedPreferences
. Wenn Sie diesem Objekt Elemente hinzufügen, werden diese tatsächlich innerhalb des Objekts hinzugefügtSharedPreferences
.Das ist in Ordnung, aber das Problem tritt auf, wenn Sie versuchen, es zu speichern: Android vergleicht das modifizierte HashSet, das Sie speichern möchten,
SharedPreferences.Editor.putStringSet
mit dem aktuellen, das auf dem gespeichert istSharedPreference
, und beide sind dasselbe Objekt !!!Eine mögliche Lösung besteht darin, eine Kopie des
Set<String>
vomSharedPreferences
Objekt zurückgegebenen Objekts zu erstellen:Set<String> s = new HashSet<String>(sharedPrefs.getStringSet("key", new HashSet<String>()));
Dadurch wird
s
ein anderes Objekt erstellt, und die hinzugefügten Zeichenfolgens
werden nicht zu der in der gespeicherten Gruppe hinzugefügtSharedPreferences
.Eine andere Problemumgehung besteht darin, dieselbe
SharedPreferences.Editor
Transaktion zum Speichern einer anderen einfacheren Einstellung (wie einer Ganzzahl oder eines Booleschen Werts) zu verwenden. Sie müssen lediglich erzwingen, dass die gespeicherten Werte bei jeder Transaktion unterschiedlich sind (z. B. können Sie die Zeichenfolge speichern eingestellte Größe).quelle
Dieses Verhalten ist so dokumentiert, dass es beabsichtigt ist:
von getStringSet:
Und es erscheint ziemlich vernünftig, insbesondere wenn es in der API dokumentiert ist, da diese API sonst bei jedem Zugriff eine Kopie erstellen müsste. Der Grund für dieses Design war also wahrscheinlich die Leistung. Ich nehme an, sie sollten dafür sorgen, dass diese Funktion ein Ergebnis zurückgibt, das in eine nicht modifizierbare Klasseninstanz eingeschlossen ist, aber dies erfordert erneut eine Zuordnung.
quelle
War auf der Suche nach einer Lösung für das gleiche Problem und löste es durch:
1) Rufen Sie den vorhandenen Satz aus den freigegebenen Einstellungen ab
2) Machen Sie eine Kopie davon
3) Aktualisieren Sie die Kopie
4) Speichern Sie die Kopie
SharedPreferences.Editor editor = sharedPrefs.edit(); Set<String> oldSet = sharedPrefs.getStringSet("key", new HashSet<String>()); //make a copy, update it and save it Set<String> newStrSet = new HashSet<String>(); newStrSet.add(new_element); newStrSet.addAll(oldSet); editor.putStringSet("key",newStrSet); edit.commit();
Warum
quelle
Ich habe alle oben genannten Antworten ausprobiert. Keine hat für mich funktioniert. Also habe ich die folgenden Schritte gemacht
Fügen Sie die in der Kopie enthaltenen Werte zu der gelöschten gemeinsamen Einstellung hinzu, die als neu behandelt wird.
public static void addCalcsToSharedPrefSet(Context ctx,Set<String> favoriteCalcList) { ctx.getSharedPreferences(FAV_PREFERENCES, 0).edit().clear().commit(); SharedPreferences sharedpreferences = ctx.getSharedPreferences(FAV_PREFERENCES, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedpreferences.edit(); editor.putStringSet(FAV_CALC_NAME, favoriteCalcList); editor.apply(); }
Ich hatte ein Problem mit den Werten, die nicht dauerhaft sind. Wenn ich die App nach dem Bereinigen der App aus dem Hintergrund erneut öffne, wird nur das erste Element angezeigt, das der Liste hinzugefügt wurde.
quelle
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() { // ... other code // mModified is a Map of all the key/values added through the various put*() methods. for (Map.Entry<String, Object> e : mModified.entrySet()) { String k = e.getKey(); Object v = e.getValue(); // ... other code // mapToWriteToDisk is a copy of the in-memory Map of our SharedPreference file's // key/value pairs. 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 vorhandenexistingValue
istnull
(noch nicht vorhanden ist) oder wenn sichexistingValue
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 gespeichertMap<String, Object> mMap;
(später beimapToWriteToDisk
jedem Versuch, in eine Datei zu schreiben, kopiert ). Wenn Sie anrufen, erhaltengetStringSet()
Sie eineSet
von dieser In-Memory-Map zurück. Wenn Sie dann derselbenSet
Instanz einen Wert hinzufügen , ändern Sie die speicherinterne Zuordnung. Wenn Sie dann aufrufeneditor.putStringSet()
und versuchen, ein Commit durchzuführen,commitToMemory()
wird es ausgeführt, und die Vergleichszeile versucht, Ihren neu geänderten Wert zu vergleichenv
,existingValue
der im Grunde derselbe im Speicher istSet
, den Sie gerade geändert haben. Die Objektinstanzen sind unterschiedlich, da dieSet
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 anrufengetStringSet()
werden Sie das gleiche im Speicher immer wiederSet
. 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 andereSet
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 Wertv
.quelle