2012-12-01 100 views
23

我已經竭盡全力讓我的Android遊戲的所有數據都融入到savedInstanceState Bundle中。完全有很多數據,包括許多Parcelable對象。這可確保在應用程序暫停或方向更改時,正在重新創建的活動不會丟失任何數據。將Bundle保存到SharedPreferences

但是,我最近才發現savedInstanceState包顯然不適合長期存儲。所以我正在尋找一種方法,使我可以將現有的存儲方法作爲長期解決方案,以便遊戲狀態始終可以恢復。

我聽說過的2個解決方案迄今:

1)使用方向變化savedInstanceState捆綁,同時也包括對SharedPrefs當應用程序需要被完全關閉。

這似乎令人難以置信適得其反,因爲它使用兩種不同的方法,完全做到基本相同的事情。另外,因爲我的savedInstanceState Bundle使用了Parcelable對象,所以我將不得不爲每個對象賦予另一個方法來使它們能夠寫入SharedPrefs。基本上很多重複和難以管理的代碼。

2)序列化savedInstanceState束和其直接寫入到文件中。

我願意接受這一點,但我真的不知道如何去這樣做。不過,我仍然抱着希望,可能會有更好的解決方案,因爲我聽說Android中的序列化是「滑稽/不可用的慢」。

如果有人能爲我提供解決方案,我將不勝感激。

+0

要序列化只是尋找一個序列化類,它不應該很難找到。我在使用它時沒有注意到任何令人生厭的延遲 – mango

+0

我能找到的唯一信息告訴我我需要實現Serializable - 但Bundle沒有實現這個接口。 – Dan

+0

我建議圖書館https://github.com/iamironz/binaryprefs,它允許通過實現持久化接口(在JDK Externalizable接口) –

回答

3

我現在拿出我自己解決這個問題,這就是節能捆綁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(); 
} 

注意,我只寫了情況,因爲我所需要的類型,但如果你有捆綁也包括其他類型的,這應該是很容易適應。

此方法將遞歸地保存存儲在給定Bundle內的其他Bundle對象。但是,它不適用於Parcelable對象,所以我必須改變我的Parcelable對象,以便將它們自己存儲到Bundle中。由於Parcel和Bundles非常相似,這並不難。不幸的是,我認爲Bundles可能比Parcels稍慢。

我然後寫構造在所有我以前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方法使用相同的技術。

+0

在這種情況下我們如何獲得捆綁? 謝謝 –

+1

我不確定你的意思到底是什麼......一旦這個包被保存到SharedPrefs,它可以像其他任何包一樣被檢索。 – Dan

+0

什麼是關鍵和o在這裏。表格你在哪裏通過這些論證 – ekjyot

18

搞怪,本週發行的Android Weekly解開這個庫47:android complex preferences

它應該適合你。

+0

保存像標準的Java數據這似乎很有希望,但很可惜,我不能讓它的工作任何東西。我嘗試過使用各種Bundle對象,包括空的對象,以及一些比較簡單的對象,但仍然沒有運氣。它在保存時要麼抱怨「循環引用」,要麼在加載時「在___鍵存儲的對象是另一個遊戲的實例」。這讓我發瘋...... – Dan

+0

請把它作爲一個單獨的問題發佈。如果您向我的地址添加評論,我會很感興趣,所以我可以關注它。 – Snicolas

+1

事實上,它只是使用GSon將所有東西都保存爲JSon ......無論如何,我的感覺是,您的數據可能是內部類的東西。這會讓你很容易循環。你的POJO分開課程嗎? – Snicolas

1

我轉達了丹的回答與功能自動重新分發包,並取得名字不太可能發生衝突。

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

https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/src-html/org/ apache/commons/lang/StringUtils.html獲得什麼StringUtils funcs正在做(不包括在android中) –

相關問題