2010-04-08 37 views
19

Tôi muốn tuần tự hóa một đối tượng Bundle, nhưng dường như không thể tìm thấy một cách đơn giản để thực hiện nó. Sử dụng Parcel dường như không phải là một tùy chọn, vì tôi muốn lưu trữ dữ liệu được tuần tự hóa vào tệp.Làm thế nào để tuần tự hóa một gói?

Bất kỳ ý tưởng nào về cách thực hiện việc này?

Lý do tôi muốn điều này là để lưu và khôi phục trạng thái hoạt động của tôi, cũng như khi nó bị người dùng giết. Tôi đã tạo một Bundle với trạng thái mà tôi muốn lưu trong onSaveInstanceState. Nhưng android chỉ giữ lại Bundle này khi hoạt động bị hệ thống giết chết. Khi người dùng giết hoạt động, tôi cần lưu trữ nó. Do đó tôi muốn serialize và lưu nó vào tập tin. Tất nhiên, nếu bạn có cách nào khác để hoàn thành điều tương tự, tôi cũng sẽ biết ơn điều đó.

Chỉnh sửa: Tôi quyết định mã hóa trạng thái của mình là JSONObject thay vì một Gói. Đối tượng JSON sau đó có thể được đặt trong một Bundle dưới dạng một Serializable, hoặc được lưu vào tệp. Có lẽ không phải là cách hiệu quả nhất, nhưng nó đơn giản, và nó có vẻ hoạt động tốt.

Trả lời

6

Tôi sử dụng SharedPreferences để tránh giới hạn đó, sử dụng cùng kiểu putXXX() và getXXX() lưu trữ và truy xuất dữ liệu như lớp Bundle và tương đối đơn giản để triển khai nếu bạn đã sử dụng Gói trước đó.

Vì vậy, trong onCreate Tôi có một kiểm tra như thế này

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

tôi lưu dữ liệu trò chơi của tôi đến một Bundle trong onSaveInstanceState(), và tải dữ liệu từ một Bundle trong onRestoreInstanceState()

Tôi cũng lưu dữ liệu trò chơi vào SharedPreferences trong onPause() và tải dữ liệu từ SharedPreferences trong onResume()

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

Tôi sẽ không ngạc nhiên nếu một số người cảm thấy đây là việc sử dụng SharedPreferences không chính xác, nhưng nó hoàn thành công việc. Tôi đã sử dụng phương pháp này trong tất cả các trò chơi của mình (gần 2 triệu lượt tải xuống) trong hơn một năm và nó hoạt động.

+2

Chắc chắn rằng các công trình, tôi đã chỉ hy vọng để tránh có 2 cách để bó nhà nước, ngay cả khi họ là rất giống nhau. – hermo

+0

Đây là chính xác những gì tôi đã có trong tâm trí để tiết kiệm một trạng thái dai dẳng. – Awemo

7

lưu trữ bất kỳ Parcelable vào một tập tin là rất dễ dàng:

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

tận hưởng!

+9

Có, tôi cũng thấy điều đó. Vấn đề là không có đảm bảo rằng bạn có thể unmarshall nó một lần nữa, nói rằng nếu hệ điều hành được cập nhật và Parcel đã thay đổi. Nhưng nếu bạn có thể sống với điều đó thì tốt thôi. – hermo

+12

Dữ liệu bạn lấy từ phương thức mashall() không được đặt trong bất kỳ loại lưu trữ liên tục nào (trên ổ đĩa cục bộ, trên mạng, v.v.). Đối với điều đó, bạn nên sử dụng serialization tiêu chuẩn hoặc một loại cơ chế serialization chung. Parcel được biểu diễn bằng marshalled được tối ưu hóa cao cho IPC cục bộ và do đó không cố gắng duy trì tính tương thích với dữ liệu được tạo trong các phiên bản khác nhau của nền tảng này. (http://developer.android.com/reference/android/os/Parcel.html#marshall()) – Oneiros

+0

Tôi cho rằng bạn không nên chuyển tệp đã lưu sang các trình phát khác, nhưng có thể sẽ ổn nếu bạn đang sử dụng trên thiết bị duy nhất (ví dụ để lưu dữ liệu tạm thời). –

4

Chuyển đổi nó để 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

Điều này một lần nữa đi ngược lại đề nghị lưu trữ nội dung của một bưu kiện vào bất kỳ hình thức bộ nhớ liên tục nào (mà Javadocs cảnh báo chống lại). Giả sử bạn đi và cập nhật hệ điều hành của bạn vì một số lý do, sau đó mã ở trên sẽ làm hỏng ứng dụng của bạn trong phương thức 'restoreFromPreferences() "hoặc trả lại một số giá trị không xác định trong gói. – Yinzara

0

Trong trường hợp bạn muốn lưu trữ nó trong lưu trữ liên tục bạn không thể dựa vào cơ chế parcelable cũng không serializable. Bạn phải làm điều đó một mình và dưới đây là cách làm thế nào tôi thường làm điều đó:

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

Bạn đại diện cho dữ liệu như mảng byte mà bạn có thể dễ dàng lưu trữ vào tập tin, gửi qua mạng hoặc lưu trữ cơ sở dữ liệu sql sử dụng ormLite như sau:

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

và SerializedItem:

public class SerializedItem { 


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

// + getters and setters 
} 

PS: đoạn code trên là phụ thuộc vào thư viện Gson (đó là khá phổ biến, chỉ để cho bạn biết).

Các vấn đề liên quan