2010-04-08 59 views
19

我想序列化一個Bundle對象,但似乎無法找到一個簡單的方法。使用Parcel似乎不是一種選擇,因爲我想將序列化的數據存儲到文件中。如何序列化一個包?

關於如何做到這一點的任何想法?

我想要這個的原因是保存和恢復我的活動狀態,當它被用戶殺死時。我已經創建了一個包含我想保存在onSaveInstanceState中的狀態的Bundle。但是,當活動被SYSTEM終止時,android只保留這個Bundle。當用戶殺死該活動時,我需要自己保存它。因此,我想序列化並將其存儲到文件中。當然,如果你有其他方式來完成同樣的事情,我也會爲此感恩。

編輯: 我決定將我的狀態編碼爲JSONObject而不是Bundle。然後可以將JSON對象作爲可序列化放入Bundle中,或存儲到文件中。可能不是最有效的方式,但它很簡單,它似乎工作正常。

回答

6

我使用SharedPreferences來解決這個限制,它使用與Bundle類一樣的存儲和檢索數據的putXXX()和getXXX()風格,如果以前使用過Bundle,實現相對簡單。

所以的onCreate我在onRestoreInstanceState()

這樣

if(savedInstanceState != null) 
{ 
    loadGameDataFromSavedInstanceState(savedInstanceState); 
} 
else 
{ 
    loadGameDataFromSharedPreferences(getPreferences(MODE_PRIVATE)); 
} 

我在我的遊戲數據保存到的onSaveInstanceState()一包檢查,並從包中加載數據

我還遊戲數據從SharedPreferences中的onResume保存到的onPause()SharedPreferences,和負載數據()

onPause() 
{ 
    // get a SharedPreferences editor for storing game data to 
    SharedPreferences.Editor mySharedPreferences = getPreferences(MODE_PRIVATE).edit(); 

    // call a function to actually store the game data 
    saveGameDataToSharedPreferences(mySharedPreferences); 

    // make sure you call mySharedPreferences.commit() at the end of your function 
} 

onResume() 
{ 
    loadGameDataFromSharedPreferences(getPreferences(MODE_PRIVATE)); 
} 

如果有人覺得這是對SharedPreferences的不正確使用,我不會感到驚訝,但它可以完成工作。我一直在我的所有遊戲(近200萬次下載)中使用這種方法一年多,並且工作正常。

+2

當然有效,我只是希望避免有兩種綁定狀態的方法,即使它們非常相似。 – hermo 2010-04-09 04:51:24

+0

這正是我想要保存持久狀態的想法。 – Awemo 2012-05-13 13:06:12

7

存儲任何Parcelable到一個文件是很容易的:

FileOutputStream fos = context.openFileOutput(localFilename, Context.MODE_PRIVATE); 
Parcel p = Parcel.obtain(); // i make an empty one here, but you can use yours 
fos.write(p.marshall()); 
fos.flush(); 
fos.close(); 

享受!

+9

是的,我也找到了。問題是,不能保證你可以再次解組它,比如操作系統被更新並且Parcel已經改變。但是如果你能忍受這一點,那就沒問題了。 – hermo 2010-04-09 04:41:03

+12

您從方法mashall()中檢索到的數據不得放置在任何類型的永久性存儲器中(在本地磁盤上,通過網絡等)。爲此,您應該使用標準序列化或其他類型的通用序列化機制。地塊編組表示針對本地IPC進行了高度優化,因此不會嘗試保持與在不同版本的平臺中創建的數據的兼容性。 (http://developer.android.com/reference/android/os/Parcel.html#marshall()) – Oneiros 2012-06-11 17:59:00

+0

我想你不應該把保存的文件轉移到其他的devies,但可能沒關係,如果你正在使用它在單個設備上(例如,用於保存臨時數據)。 – 2012-08-24 12:08:56

4

將其轉換爲SharedPreferences:

private void saveToPreferences(Bundle in) { 
    Parcel parcel = Parcel.obtain(); 
    String serialized = null; 
    try { 
     in.writeToParcel(parcel, 0); 

     ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
     IOUtils.write(parcel.marshall(), bos); 

     serialized = Base64.encodeToString(bos.toByteArray(), 0); 
    } catch (IOException e) { 
     Log.e(getClass().getSimpleName(), e.toString(), e); 
    } finally { 
     parcel.recycle(); 
    } 
    if (serialized != null) { 
     SharedPreferences settings = getSharedPreferences(PREFS, 0); 
     Editor editor = settings.edit(); 
     editor.putString("parcel", serialized); 
     editor.commit(); 
    } 
} 

private Bundle restoreFromPreferences() { 
    Bundle bundle = null; 
    SharedPreferences settings = getSharedPreferences(PREFS, 0); 
    String serialized = settings.getString("parcel", null); 

    if (serialized != null) { 
     Parcel parcel = Parcel.obtain(); 
     try { 
      byte[] data = Base64.decode(serialized, 0); 
      parcel.unmarshall(data, 0, data.length); 
      parcel.setDataPosition(0); 
      bundle = parcel.readBundle(); 
     } finally { 
      parcel.recycle(); 
     } 
    } 
    return bundle; 
} 
+4

這又違背了將包裹內容存儲到任何形式的持久性內存(Javadocs警告)的建議。假設你出於某種原因去更新操作系統,那麼上面的代碼會在'restoreFromPreferences()「方法中崩潰你的應用程序,或者返回一些未知的值。 – Yinzara 2013-01-29 10:28:43

0

如果你想將其存儲在持久性存儲,你不能依靠parcelable也不序列化機制。你必須自己做,下面是怎樣的方式,我通常做:

private static final Gson sGson = new GsonBuilder().create(); 
private static final String CHARSET = "UTF-8"; 
// taken from http://www.javacamp.org/javaI/primitiveTypes.html 
private static final int BOOLEAN_LEN = 1; 
private static final int INTEGER_LEN = 4; 
private static final int DOUBLE_LEN = 8; 

public static byte[] serializeBundle(Bundle bundle) { 
    try { 
     List<SerializedItem> list = new ArrayList<>(); 
     if (bundle != null) { 
      Set<String> keys = bundle.keySet(); 
      for (String key : keys) { 
       Object value = bundle.get(key); 
       if (value == null) continue; 
       SerializedItem bis = new SerializedItem(); 
       bis.setClassName(value.getClass().getCanonicalName()); 
       bis.setKey(key); 
       if (value instanceof String) 
        bis.setValue(((String) value).getBytes(CHARSET)); 
       else if (value instanceof SpannableString) { 
        String str = Html.toHtml((Spanned) value); 
        bis.setValue(str.getBytes(CHARSET)); 
       } else if (value.getClass().isAssignableFrom(Integer.class)) { 
        ByteBuffer b = ByteBuffer.allocate(INTEGER_LEN); 
        b.putInt((Integer) value); 
        bis.setValue(b.array()); 
       } else if (value.getClass().isAssignableFrom(Double.class)) { 
        ByteBuffer b = ByteBuffer.allocate(DOUBLE_LEN); 
        b.putDouble((Double) value); 
        bis.setValue(b.array()); 
       } else if (value.getClass().isAssignableFrom(Boolean.class)) { 
        ByteBuffer b = ByteBuffer.allocate(INTEGER_LEN); 
        boolean v = (boolean) value; 
        b.putInt(v ? 1 : 0); 
        bis.setValue(b.array()); 
       } else 
        continue; // we do nothing in this case since there is amazing amount of stuff you can put into bundle but if you want something specific you can still add it 
//      throw new IllegalStateException("Unable to serialize class + " + value.getClass().getCanonicalName()); 

       list.add(bis); 
      } 
      return sGson.toJson(list).getBytes(CHARSET); 
     } 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    throw new IllegalStateException("Unable to serialize " + bundle); 
} 

public static Bundle deserializeBundle(byte[] toDeserialize) { 
    try { 
     Bundle bundle = new Bundle(); 
     if (toDeserialize != null) { 
      SerializedItem[] bundleItems = new Gson().fromJson(new String(toDeserialize, CHARSET), SerializedItem[].class); 
      for (SerializedItem bis : bundleItems) { 
       if (String.class.getCanonicalName().equals(bis.getClassName())) 
        bundle.putString(bis.getKey(), new String(bis.getValue())); 
       else if (Integer.class.getCanonicalName().equals(bis.getClassName())) 
        bundle.putInt(bis.getKey(), ByteBuffer.wrap(bis.getValue()).getInt()); 
       else if (Double.class.getCanonicalName().equals(bis.getClassName())) 
        bundle.putDouble(bis.getKey(), ByteBuffer.wrap(bis.getValue()).getDouble()); 
       else if (Boolean.class.getCanonicalName().equals(bis.getClassName())) { 
        int v = ByteBuffer.wrap(bis.getValue()).getInt(); 
        bundle.putBoolean(bis.getKey(), v == 1); 
       } else 
        throw new IllegalStateException("Unable to deserialize class " + bis.getClassName()); 
      } 
     } 
     return bundle; 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    throw new IllegalStateException("Unable to deserialize " + Arrays.toString(toDeserialize)); 
} 

您表示數據的字節數組,你可以很容易地存儲文件,通過網絡發送或使用ormLite作爲存儲到SQL數據庫如下:

@DatabaseField(dataType = DataType.BYTE_ARRAY) 
    private byte[] mRawBundle; 

和SerializedItem:

public class SerializedItem { 


private String mClassName; 
private String mKey; 
private byte[] mValue; 

// + getters and setters 
} 

PS:上面的代碼依賴於GSON庫(這是非常常見的,只是爲了讓你知道)。

相關問題