Сохранить собственный массив объектов в состоянии экземпляра

У меня есть List<CustomObject> (где CustomObject поступает из внешней библиотеки - я не могу вносить в него изменения). Я хочу сохранить это в onSaveInstanceState(Bundle), но я не могу это сделать. Вот варианты, которые я пробовал:

outState.putSerializable(KEY, (ArrayList<CustomObject>) myList); // because myList is instantiated as an ArrayList
outState.putSerializable(KEY, myList.toArray());

Обе опции работают при переключении ориентации на телефоне (да, onSaveInstanceState вызывается при переключении ориентации - я проверил в logcat). Однако, когда текущая активность пытается запустить другую (с startActivity(Intent)), Android приостанавливает текущую активность и вызывает onSaveInstanceState() снова. На этот раз он не работает, по какой-то причине мне неизвестен. Любопытно, что onSaveInstanceState() выполняется успешно. Распечатка трассировки стека не указывает на мой код:

E/AndroidRuntime(23898): java.lang.RuntimeException: Parcel: unable to marshal value [email protected]
E/AndroidRuntime(23898):    at android.os.Parcel.writeValue(Parcel.java:1087)
E/AndroidRuntime(23898):    at android.os.Parcel.writeArray(Parcel.java:519)
E/AndroidRuntime(23898):    at android.os.Parcel.writeValue(Parcel.java:1072)
E/AndroidRuntime(23898):    at android.os.Parcel.writeMapInternal(Parcel.java:469)
E/AndroidRuntime(23898):    at android.os.Bundle.writeToParcel(Bundle.java:1445)
E/AndroidRuntime(23898):    at android.os.Parcel.writeBundle(Parcel.java:483)
E/AndroidRuntime(23898):    at android.app.ActivityManagerProxy.activityPaused(ActivityManagerNative.java:1427)
E/AndroidRuntime(23898):    at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:3106)
E/AndroidRuntime(23898):    at android.app.ActivityThread.access$2400(ActivityThread.java:119)
E/AndroidRuntime(23898):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1870)
E/AndroidRuntime(23898):    at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime(23898):    at android.os.Looper.loop(Looper.java:123)
E/AndroidRuntime(23898):    at android.app.ActivityThread.main(ActivityThread.java:4363)
E/AndroidRuntime(23898):    at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(23898):    at java.lang.reflect.Method.invoke(Method.java:521)
E/AndroidRuntime(23898):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
E/AndroidRuntime(23898):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
E/AndroidRuntime(23898):    at dalvik.system.NativeStart.main(Native Method)

Есть ли способ сохранить пользовательские объекты в состоянии экземпляра?

Ответы

Ответ 1

Создайте CustomObject CustomObject Parcelable и используйте:

outState.putParcelable(KEY, myList);
onSaveInstanceState(outState);

Также проверьте этот учебник.

ИЗМЕНИТЬ после комментария CommonsWare:

Если ваш CustomObject не реализует Serializable или Parcelable, я бы попытался обернуть его внутри собственного объекта и добавить:

  • private void readObject(ObjectInputStream aStream) throws IOException, ClassNotFoundException { /*Your deserialization */ }
  • private void writeObject(ObjectOutputStream aStream) throws IOException { /*Your serialization */}

Ответ 2

Удерживайте List<CustomObject> службу и сделайте ее доступной для ваших действий через локальный шаблон привязки.

Не только вам не нужно беспокоиться о том, чтобы удерживать его в своем состоянии экземпляра, но вы немного лучше контролируете время жизни этих объектов в памяти. Время жизни экземпляра контролируется Android; как долго a Service держится на объектах, контролируется вами. В частности, если CustomObject может быть большим, или список может быть длинным, я бы предпочел, чтобы у вас был больший контроль над тем, сколько времени потребляется ОЗУ.

Ответ 3

Если это прежде всего для обработки изменений ориентации, можно Activity#onRetainNonConfigurationInstance() делать то, что вы хотите?

активность может использовать этот API для распространения расширенного состояния от старого к новому экземпляру активности, от загруженных растровых изображений, до сетевых подключений, к равномерно активному запуску потоков. Обратите внимание, что вам не следует распространять данные, которые могут меняться в зависимости от конфигурации, включая любые данные, загруженные из таких ресурсов, как строки, макеты или чертежи.

Этот API не поможет вам, если вы пытаетесь сделать больше, чем сохраняете данные при изменении конфигурации.

Ответ 4

Насколько я знаю, SavedInstanceState предназначен для сохранения конфигурации пользовательского интерфейса активности (например, стандартные виджеты пользовательского интерфейса Android, например текстовое поле, автоматически сохраняются).

Если вы хотите сохранить пользовательский объект между различными перезагрузками активности (это не относится к завершению деятельности, инициированной пользователем, нажав кнопку "Назад", но она применяется, например, для изменений ориентации). Используйте следующий код для сохранения объекта:

// maintain a reference to the EchoServer object when the activity is recreated
@Override
public Object onRetainNonConfigurationInstance() {
    return <<your object of choice>>;
}

И в методе onCreate (Bundle savedInstanceState) вы можете получить объект:

    // if there is a saved instance state, restore the state
    if (savedInstanceState != null) {
        <<yourObject>> = (<<your object class) getLastNonConfigurationInstance();

Ответ 5

Почему бы просто не сохранить объект на SD-карту? Вы можете увидеть пример того, как я использую это в своем блоге → http://androidworkz.com/2010/07/06/source-code-imageview-flipper-sd-card-scanner/

public void saveArray(String filename, String[] output_field) {
         try {
            FileOutputStream fos = new FileOutputStream(filename);
            GZIPOutputStream gzos = new GZIPOutputStream(fos);
            ObjectOutputStream out = new ObjectOutputStream(gzos);
            out.writeObject(output_field);
            out.flush();
            out.close();
         }
         catch (IOException e) {
             e.getStackTrace(); 
         }
      }

    public String[] loadArray(String filename) {
          try {
            FileInputStream fis = new FileInputStream(filename);
            GZIPInputStream gzis = new GZIPInputStream(fis);
            ObjectInputStream in = new ObjectInputStream(gzis);
            String[] read_field = (String[])in.readObject();
            in.close();
            return read_field;
          }
          catch (Exception e) {
              e.getStackTrace();
          }
          return null;
      }