将Bundle保存到SharedPreferences

我已经竭尽全力让我的Android游戏的所有数据都适合saveInstanceState Bundle。 总共有很多数据,包括许多Parcelable对象。 这可确保当应用程序暂停或方向更改时,重新创建的活动不会丢失任何数据。

但是,我刚刚发现savedInstanceState包显然不适合长期存储。 所以我正在寻找一种方法,我可以调整我现有的保存方法作为长期解决方案,以便始终可以恢复游戏状态。

到目前为止,我听说过2个解决方案:

1)使用savedInstanceState包进行方向更改,但在需要完全关闭应用程序时还要包含SharedPrefs。

这看起来非常适得其反,因为它使用2种不同的完全方法来做同样的事情。 此外,由于我的savedInstanceState Bundle使用Parcelable对象,因此我必须为这些对象中的每一个提供另一种方法,以使它们能够写入SharedPrefs。 基本上很多重复和难以管理的代码。

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

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

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

有趣的是,本周, Android Weekly发布了47个这个库: android复杂偏好 。

它应该适合你。

我现在已经提出了我自己的解决这个问题的方法,这是一种将Bundles保存到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 keySet = gameState.keySet(); Iterator 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的Bundles,这应该很容易适应。

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

然后,我在所有以前的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的答案,其function是自动重新创建Bundles,并使名称不太可能发生冲突。

 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 keySet = preferences.keySet(); Iterator 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 all = sharedPreferences.getAll(); Iterator it = all.keySet().iterator(); String prefKeyPrefix = key + SAVED_PREFS_BUNDLE_KEY_SEPARATOR; Set subBundleKeys = new HashSet(); 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; }