提问人:Dan 提问时间:12/1/2012 最后编辑:Dan 更新时间:5/30/2016 访问量:28381
将 Bundle 保存到 SharedPreferences [duplicate]
Save Bundle to SharedPreferences [duplicate]
问:
我竭尽全力使我的 Android 游戏的所有数据都适合 savedInstanceState Bundle。总共有很多数据,包括许多 Parcelable 对象。这可确保在应用程序暂停或方向更改时,重新创建的 Activity 不会丢失任何数据。
但是,我最近才发现 savedInstanceState 捆绑包显然不适合长期存储。因此,我正在寻找一种方法,可以调整我现有的保存方法,使其成为长期解决方案,以便始终可以恢复游戏状态。
到目前为止,我听说过 2 种解决方案:
1) 使用 savedInstanceState 捆绑包进行方向更改,但也在需要完全关闭应用时合并 SharedPrefs。
这似乎适得其反,因为它使用 2 种不同的完全方法来做基本上相同的事情。此外,由于我的 savedInstanceState Bundle 使用 Parcelable 对象,因此我必须为每个对象提供另一种方法,以便将它们写入 SharedPrefs。本质上是大量重复且难以管理的代码。
2) 序列化 savedInstanceState Bundle 并将其直接写入文件。
我对此持开放态度,但我实际上不知道该怎么做。但是,我仍然希望可能有更好的解决方案,因为我听说 Android 中的序列化“滑稽/无法使用”。
如果有人能为我提供解决方案,我将不胜感激。
答:
有趣的是,本周,Android Weekly 的第 47 期发布了这个库:android complex preferences。
它应该适合你。
评论
我现在想出了自己的解决方案,这是一种将 Bundle 保存到 SharedPreferences 的半自动方法。我说半自动是因为,虽然保存 Bundle 只需要一种方法,但再次检索数据并将其转换回 Bundle 需要一些工作。
以下是保存 Bundle 的代码:
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();
}
请注意,我只为我需要的类型编写了案例,但如果您的 Bundle 也包含其他类型,这应该很容易适应。
此方法将递归保存存储在给定 Bundle 中的其他 Bundle 对象。但是,它不适用于 Parcelable 对象,因此我不得不更改我的 Parcelable 对象,使它们将自己存储到 Bundle 中。由于 Parcels 和 Bundles 非常相似,这并不难。不幸的是,我认为 Bundles 也可能比 Parcels 稍慢。
然后,我在所有以前的 Parcelable 对象中编写了构造函数,使它们能够从存储的数据 SharedPreferences 中重新捆绑自己。重建所需数据的密钥非常容易。假设您具有以下数据结构:
Bundle b {
KEY_X -> int x;
KEY_Y -> Bundle y {
KEY_Z -> int z;
}
}
这些将保存到 SharedPreferences,如下所示:
KEY_X -> x
KEY_YKEY_Z -> z
它可能不是世界上最漂亮的方法,但它有效,而且它花费的代码比替代方案少得多,因为现在我的 onSaveInstanceState 方法和我的 onPause 方法使用相同的技术。
评论
我扩展了 Dan 的答案,使用一个自动重新创建捆绑包的功能,并使名称不太可能发生冲突。
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;
}
评论