Как сериализовать пакет?

Я хотел бы сериализовать объект Bundle, но не могу найти простой способ сделать это. Использование Parcel не похоже на вариант, так как я хочу хранить сериализованные данные в файл.

Есть идеи о том, как это сделать?

Причина, по которой я хочу, это сохранить и восстановить состояние моей активности, также когда оно было убито пользователем. Я уже создаю Bundle с состоянием, которое хочу сохранить в onSaveInstanceState. Но андроид только держит этот пакет, когда активность убита Системой. Когда пользователь убивает активность, мне нужно сохранить ее самостоятельно. Поэтому я хотел бы сериализовать и хранить его в файле. Конечно, если у вас есть другой способ выполнить одно и то же, я тоже буду благодарен за это.

Изменить: я решил закодировать свое состояние как JSONObject, а не Bundle. Объект JSON может быть помещен в Bundle как Serializable или сохранен в файл. Наверное, это не самый эффективный способ, но он прост, и, похоже, он работает нормально.

Сохранение любого 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(); 

наслаждаться!

Я использую SharedPreferences, чтобы обойти это ограничение, он использует те же методы putXXX () и getXXX () хранения и получения данных, что и класс Bundle, и относительно прост в реализации, если раньше вы использовали Bundle.

Итак, в onCreate у меня есть чек, подобный этому

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

Я сохраняю свои данные игры в Bundle в onSaveInstanceState () и загружаю данные из Bundle в onRestoreInstanceState ()

А ТАКЖЕ

Я также сохраняю данные игры в SharedPreferences в onPause () и загружаю данные из SharedPreferences в 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)); } 

Я не удивлюсь, если некоторые люди считают, что это неправильное использование SharedPreferences, но оно выполняет свою работу. Я использую этот метод во всех моих играх (почти 2 миллиона загрузок) больше года, и он работает.

Преобразуйте его в 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; } 

Если вы хотите сохранить его в постоянном хранилище, вы не можете полагаться на произвольный или сериализуемый механизм. Вы должны сделать это самостоятельно, а ниже – то, как я обычно это делаю:

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

Вы представляете данные в виде массива байтов, которые вы можете легко хранить в файле, отправлять по сети или хранить в базе данных sql с помощью ormLite следующим образом:

  @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 (что довольно часто, просто чтобы вы знали).