25

AndroidゲームのすべてのデータをsavedInstanceStateバンドルに収めるために、かなりの時間を費やしました。多くのParcelableオブジェクトを含め、全体として多くのデータがあります。これにより、アプリが一時停止したり、向きが変わったりしたときに、アクティビティが再作成されてもデータが失われることはありません。

ただし、最近、savedInstanceStateバンドルが長期保存に適していないことを発見しました。そのため、ゲームの状態を常に復元できるように、既存の保存方法を長期的なソリューションとして機能するように適応させる方法を探しています。

私はこれまでに2つの解決策について聞いたことがあります:

1)向きの変更にはsavedInstanceStateバンドルを使用しますが、アプリを完全にシャットダウンする必要がある場合はSharedPrefsも組み込みます。

これは、基本的に同じことを行うために2つの異なる完全な方法を使用するため、信じられないほど逆効果のようです。また、savedInstanceStateバンドルはParcelableオブジェクトを使用するため、これらの各オブジェクトに別のメソッドを指定して、SharedPrefsに書き込むことができるようにする必要があります。本質的に、重複した管理が難しいコードがたくさんあります。

2)savedInstanceStateバンドルをシリアル化し、ファイルに直接書き込みます。

私はこれを受け入れていますが、実際にそれを行う方法がわかりません。ただし、Androidでのシリアル化は「コミカルに/使用できないほど遅い」と聞いているので、もっと良い解決策があるかもしれないという希望を抱いています。

誰かが私にこれに対する解決策を提供してくれたら、私は非常にありがたいです。

4

3 に答える 3

17

おかしい、今週、Android Weeklyの第47号は、このライブラリを解き放ちました: androidcomplexpreferences

それはあなたに合うはずです。

于 2012-12-01T15:44:05.813 に答える
4

私は今、この問題に対する独自の解決策を考え出しました。これは、バンドルをSharedPreferencesに保存する半自動の手段です。バンドルの保存に必要なメソッドは1つだけですが、データを再度取得してバンドルに戻すには多少の作業が必要になるため、半自動と言います。

バンドルを保存するコードは次のとおりです。

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

必要なタイプのケースのみを記述していることに注意してください。ただし、他のタイプも含むバンドルがある場合、これは簡単に適応できるはずです。

このメソッドは、指定されたバンドル内に格納されている他のバンドルオブジェクトを再帰的に保存します。ただし、Parcelableオブジェクトでは機能しないため、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メソッドが同じ手法を使用しているため、他のメソッドよりもはるかに少ないコードで済みます。

于 2012-12-03T21:42:47.273 に答える
2

バンドルを自動的に再作成する機能を使用して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;
}
于 2015-01-29T17:05:34.143 に答える