保存捆绑到SharedPreferences

我已经竭尽全力将我的Android游戏的所有数据放入savedInstanceState Bundle中。 有很多数据,包括许多Parcelable对象。 这可以确保当应用程序暂停或方向更改时,正在重新创build的活动不会丢失任何数据。

不过,我刚刚发现一个savedInstanceState包显然不适合长期存储。 所以我正在寻找一种方法来适应我现有的保存方法,作为一个长期的解决scheme,以便游戏状态可以随时恢复。

到目前为止我听说过两个解决scheme:

1)使用savedInstanceState包进行方向更改,但也需要合并SharedPrefs,以便完全closures应用程序。

这似乎是相当的反作用,因为它使用两种不同的完全方法来做基本相同的事情。 另外,由于我的savedInstanceState Bundle使用了Parcelable对象,我将不得不为每个对象赋予另一个方法来使它们能够写入SharedPrefs。 基本上是很多重复和难以pipe理的代码。

2)序列化savedInstanceState Bundle并直接写入文件。

我对此持开放态度,但我实际上并不知道如何去做。 不过,我仍然抱着希望,可能有更好的解决scheme,因为我听说Android中的序列化是“滑稽/不可用慢”。

如果有人能为我提供解决scheme,我将非常感激。

Solutions Collecting From Web of "保存捆绑到SharedPreferences"

有趣的是,本周“ Android周刊 ”第47期发布了这个库: Android复杂的偏好 。

它应该适合你。

我现在想出了自己的解决scheme,这是一个将Bundles保存到SharedPreferences的半自动方法。 我说半自动化,因为虽然保存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(); } 

请注意,我只为我需要的types编写了案例,但是如果您有包含其他types的Bundle,则这应该很容易适应。

此方法将recursion地保存存储在给定Bundle内的其他Bundle对象。 然而,它不能用于Parcelable对象,所以我不得不改变我的Parcelable对象,使它们自己存储到Bundle中。 由于Parcel和Bundles非常相似,所以这并不难。 不幸的是,我认为Bundles可能比Parcels稍慢。

然后我写了所有我以前的Parcelable对象中的构造函数,使它们能够从存储SharedPreferences的数据中重新捆绑自己。 很容易就可以重build所需数据的密钥。 假设你有以下数据结构:

 Bundle b { KEY_X -> int x; KEY_Y -> Bundle y { KEY_Z -> int z; } } 

这些将被保存到SharedPreferences,如下所示:

 KEY_X -> x KEY_YKEY_Z -> z 

它可能不是世界上最漂亮的方法,但它的工作原理,它比我们更less的代码,因为现在我的onSaveInstanceState方法和我的onPause方法使用相同的技术。

我用Dan的答案扩展了一个函数来自动重新创buildBundles,并且使得名字不太可能发生冲突。

 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; }