android - retrieve - Guardar lote en SharedPreferences
sharedpreferences editor (3)
Esta pregunta ya tiene una respuesta aquí:
- Guardando un paquete completo en SharedPreferences 2 respuestas
He hecho todo lo posible para que todos los datos de mi juego de Android encajen en un paquete de estado de emergencia guardado. Hay una gran cantidad de datos en total, incluidos muchos objetos parcelables. Esto garantiza que cuando la aplicación se detiene o cambia la orientación, la actividad que se está recreando no pierde ningún dato.
Sin embargo, recientemente descubrí que un paquete savedInstanceState aparentemente NO es apropiado para el almacenamiento a largo plazo. Así que estoy buscando una manera de adaptar mi método de guardado existente para que funcione como una solución a largo plazo, para que el estado del juego siempre pueda restaurarse.
He oído hablar de 2 soluciones hasta ahora:
1) Utilice el paquete savedInstanceState para los cambios de orientación, pero también incorpore SharedPrefs para cuando la aplicación deba cerrarse por completo.
Esto parece increíblemente contraproducente, ya que utiliza 2 métodos completamente diferentes para hacer básicamente lo mismo. Además, dado que mi paquete SavedInstanceState utiliza objetos parcelables, tendría que dar a cada uno de esos objetos otro método para permitir que se escriban en SharedPrefs. Esencialmente un montón de código duplicado y difícil de administrar.
2) Serializar el paquete savedInstanceState y escribirlo directamente en un archivo.
Estoy abierto a esto, pero en realidad no sé cómo hacerlo. Sin embargo, sigo manteniendo la esperanza de que pueda haber una mejor solución, ya que he escuchado que la serialización en Android es "cómicamente / no es muy lenta".
Estaría extremadamente agradecido si alguien pudiera brindarme una solución para esto.
Ahora he encontrado mi propia solución a este problema, que es un medio semiautomático para guardar paquetes en SharedPreferences. Digo semiautomático porque, aunque guardar el paquete requiere solo un método, recuperar los datos nuevamente y convertirlos en un paquete requiere algo de trabajo.
Aquí está el código para guardar el paquete:
SharedPreferences save = getSharedPreferences(SAVE, MODE_PRIVATE);
Editor ed = save.edit();
saveBundle(ed, "", gameState);
/**
* Manually save a Bundle object to SharedPreferences.
* @param ed
* @param header
* @param gameState
*/
private void saveBundle(Editor ed, String header, Bundle gameState) {
Set<String> keySet = gameState.keySet();
Iterator<String> it = keySet.iterator();
while (it.hasNext()){
key = it.next();
o = gameState.get(key);
if (o == null){
ed.remove(header + key);
} else if (o instanceof Integer){
ed.putInt(header + key, (Integer) o);
} else if (o instanceof Long){
ed.putLong(header + key, (Long) o);
} else if (o instanceof Boolean){
ed.putBoolean(header + key, (Boolean) o);
} else if (o instanceof CharSequence){
ed.putString(header + key, ((CharSequence) o).toString());
} else if (o instanceof Bundle){
saveBundle(header + key, ((Bundle) o));
}
}
ed.commit();
}
Tenga en cuenta que solo he escrito casos para los tipos que necesitaba, pero esto debería ser fácilmente adaptable si tiene paquetes que también incluyan otros tipos.
Este método guardará recursivamente otros objetos Bundle almacenados dentro del Bundle dado. Sin embargo, no funcionará para los objetos Parcelable, por lo que tuve que modificar mis objetos Parcelable para que se almacenaran en un Bundle. Dado que los paquetes y los paquetes son bastante similares, esto no fue demasiado difícil. Creo que los paquetes también pueden ser un poco más lentos que los paquetes, desafortunadamente.
Luego escribí constructores en todos mis objetos anteriormente parcelables para permitirles volver a agruparse a partir de los datos almacenados en las preferencias compartidas. Es bastante fácil reconstruir las claves de los datos que necesita. Digamos que tienes la siguiente estructura de datos:
Bundle b {
KEY_X -> int x;
KEY_Y -> Bundle y {
KEY_Z -> int z;
}
}
Estos se guardarán en SharedPreferences de la siguiente manera:
KEY_X -> x
KEY_YKEY_Z -> z
Puede que no sea el método más bonito del mundo, pero funciona, y me cuesta mucho menos código que la alternativa, ya que ahora mi método onSaveInstanceState y mis métodos onPause utilizan la misma técnica.
Es curioso, esta semana, el número 47 de Android Weekly desató esta biblioteca: las complejas preferencias de Android .
Debe encajar para usted.
Extendí la respuesta de Dan con una función para recrear los Paquetes automáticamente, e hice los nombres menos propensos a entrar en conflicto.
private static final String SAVED_PREFS_BUNDLE_KEY_SEPARATOR = "§§";
/**
* Save a Bundle object to SharedPreferences.
*
* NOTE: The editor must be writable, and this function does not commit.
*
* @param editor SharedPreferences Editor
* @param key SharedPreferences key under which to store the bundle data. Note this key must
* not contain ''§§'' as it''s used as a delimiter
* @param preferences Bundled preferences
*/
public static void savePreferencesBundle(SharedPreferences.Editor editor, String key, Bundle preferences) {
Set<String> keySet = preferences.keySet();
Iterator<String> it = keySet.iterator();
String prefKeyPrefix = key + SAVED_PREFS_BUNDLE_KEY_SEPARATOR;
while (it.hasNext()){
String bundleKey = it.next();
Object o = preferences.get(bundleKey);
if (o == null){
editor.remove(prefKeyPrefix + bundleKey);
} else if (o instanceof Integer){
editor.putInt(prefKeyPrefix + bundleKey, (Integer) o);
} else if (o instanceof Long){
editor.putLong(prefKeyPrefix + bundleKey, (Long) o);
} else if (o instanceof Boolean){
editor.putBoolean(prefKeyPrefix + bundleKey, (Boolean) o);
} else if (o instanceof CharSequence){
editor.putString(prefKeyPrefix + bundleKey, ((CharSequence) o).toString());
} else if (o instanceof Bundle){
savePreferencesBundle(editor, prefKeyPrefix + bundleKey, ((Bundle) o));
}
}
}
/**
* Load a Bundle object from SharedPreferences.
* (that was previously stored using savePreferencesBundle())
*
* NOTE: The editor must be writable, and this function does not commit.
*
* @param sharedPreferences SharedPreferences
* @param key SharedPreferences key under which to store the bundle data. Note this key must
* not contain ''§§'' as it''s used as a delimiter
*
* @return bundle loaded from SharedPreferences
*/
public static Bundle loadPreferencesBundle(SharedPreferences sharedPreferences, String key) {
Bundle bundle = new Bundle();
Map<String, ?> all = sharedPreferences.getAll();
Iterator<String> it = all.keySet().iterator();
String prefKeyPrefix = key + SAVED_PREFS_BUNDLE_KEY_SEPARATOR;
Set<String> subBundleKeys = new HashSet<String>();
while (it.hasNext()) {
String prefKey = it.next();
if (prefKey.startsWith(prefKeyPrefix)) {
String bundleKey = StringUtils.removeStart(prefKey, prefKeyPrefix);
if (!bundleKey.contains(SAVED_PREFS_BUNDLE_KEY_SEPARATOR)) {
Object o = all.get(prefKey);
if (o == null) {
// Ignore null keys
} else if (o instanceof Integer) {
bundle.putInt(bundleKey, (Integer) o);
} else if (o instanceof Long) {
bundle.putLong(bundleKey, (Long) o);
} else if (o instanceof Boolean) {
bundle.putBoolean(bundleKey, (Boolean) o);
} else if (o instanceof CharSequence) {
bundle.putString(bundleKey, ((CharSequence) o).toString());
}
}
else {
// Key is for a sub bundle
String subBundleKey = StringUtils.substringBefore(bundleKey, SAVED_PREFS_BUNDLE_KEY_SEPARATOR);
subBundleKeys.add(subBundleKey);
}
}
else {
// Key is not related to this bundle.
}
}
// Recursively process the sub-bundles
for (String subBundleKey : subBundleKeys) {
Bundle subBundle = loadPreferencesBundle(sharedPreferences, prefKeyPrefix + subBundleKey);
bundle.putBundle(subBundleKey, subBundle);
}
return bundle;
}