【问题标题】:Save Bundle to SharedPreferences [duplicate]将捆绑包保存到 SharedPreferences [重复]
【发布时间】:2012-11-19 14:22:22
【问题描述】:

我已竭尽全力将我的 Android 游戏的所有数据都放入 savedInstanceState Bundle。总共有很多数据,包括许多 Parcelable 对象。这样可以确保在应用暂停或方向更改时,重新创建的 Activity 不会丢失任何数据。

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

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

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

这似乎会适得其反,因为它使用两种完全不同的方法来完成基本相同的事情。此外,由于我的 savedInstanceState Bundle 使用 Parcelable 对象,我必须为这些对象中的每一个提供另一种方法,以使它们能够写入 SharedPrefs。本质上是大量重复且难以管理的代码。

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

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

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

【问题讨论】:

  • 序列化只要找一个序列化类,应该不难找到。我在使用它时没有注意到任何令人难以忍受的延迟
  • 我能找到的唯一信息告诉我我需要实现 Serializable - 但 Bundle 没有实现这个接口。
  • 我推荐库github.com/iamironz/binaryprefs,它允许通过实现Persistable接口(jdk中的Externalizable接口)像标准java一样保存数据

标签: android save bundle sharedpreferences


【解决方案1】:

有趣的是,本周,Android Weekly 的第 47 期发布了这个库:android complex preferences

它应该适合你。

【讨论】:

  • 这看起来很有希望,但是,我不能让它适用于任何事情。我尝试了各种 Bundle 对象,包括空对象和一些更简单的对象,如 Points,但仍然没有运气。它要么在保存时抱怨“循环引用”,要么在加载时抱怨“存储在键___处的对象是另一个游戏的实例”。这让我发疯......
  • 请将此作为单独的问题发布。如果您在我的地址中添加评论以便我可以关注它,我会很感兴趣。
  • 确实,它只是使用 GSon 将所有内容保存为 JSon...无论如何,我的感觉是您的数据可能是某些东西的内部类。这会让你很容易地循环。你的 POJO 是单独的类吗?
  • 这是新问题:stackoverflow.com/questions/13665389/… 顺便感谢您的帮助。另外,我在之前的评论中写了“instanceof another game”,应该是“instanceof another class”。
【解决方案2】:

我现在想出了我自己的解决方案来解决这个问题,这是一种将 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();
}

请注意,我只为我需要的类型编写了案例,但如果您的 Bundles 还包含其他类型,这应该很容易适应。

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

然后,我在所有以前的 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 方法使用相同的技术。

【讨论】:

  • 在这种情况下我们如何获取Bundle?谢谢
  • 我不确定你的确切意思...一旦包被保存到 SharedPrefs,它就可以像任何其他包一样被检索。
  • 这里的关键是什么。形成你在哪里传递这些争论
  • 你能告诉我如何从共享首选项中获取捆绑包
  • 对不起!!我丢失了原始代码,但我已经用我最好的猜测编辑了这篇文章。要从 SharedPreferences 中检索 Bundle,您必须手动重新创建密钥,如我的帖子底部所述,然后使用这些密钥检索您想要的内容。我开始认为将 Bundle 保存到 SharedPreferences 毕竟不是一个明智的主意:)
【解决方案3】:

我扩展了 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;
}

【讨论】:

猜你喜欢
  • 2017-11-20
  • 1970-01-01
  • 2015-04-29
  • 2017-01-05
  • 1970-01-01
  • 2019-12-16
  • 2011-10-26
相关资源
最近更新 更多