将Bundle保存到SharedPreferences

时间:2012-12-01 15:33:28

标签: android save bundle sharedpreferences

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

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

到目前为止,我听说过两种解决方案:

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

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

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

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

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

3 个答案:

答案 0 :(得分:18)

有趣的是,本周,Android Weekly的第47期释放了这个图书馆:android complex preferences

它应该适合你。

答案 1 :(得分:3)

我现在已经提出了我自己的解决方案,这是一种将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<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();
}

请注意,我只为我需要的类型编写了案例,但是如果你有包含其他类型的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方法使用相同的技术。

答案 2 :(得分:1)

我扩展了Dan的答案,其功能是自动重新创建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<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;
}