开发者

Save custom object array in instance state

I have a List<CustomObject> (where CustomObject comes from an external library -- I can't make changes to it). I want to save this in onSaveInstanceState(Bundle), but I can't seem to do it. Here are the options that I've tried:

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

Both options work when switching orientation on the phone (yes, onSaveInstanceState is called when switching orientation -- I checked in logcat). However, when the current activity tries to start another one (with startActivity(Intent)), Android pauses the current activity and calls onSaveInstanceState() again. This time, it fails, for some reason unknown to me. The fishy thing is that onSaveInstanceState() executes successfully. The stack trace printed doesn't point to any of my code:

E/And开发者_如何学CroidRuntime(23898): java.lang.RuntimeException: Parcel: unable to marshal value my.custom.Object@5e07e43b
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)

Is there any way to store custom objects in the instance state?


Make your CustomObject implement Parcelable and use:

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

Also check this tutorial.

EDIT after CommonsWare comment:

If your CustomObject doesn't implement Serializable or Parcelable I would try wrapping it inside an object of your own and add:

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


Have your List<CustomObject> be held by a service and make it accessible to your activities via the local binding pattern.

Not only do you not have to worry about holding onto it in your instance state, but you have a bit better control over the lifetime of those objects in memory. Instance state lifetime is controlled by Android; how long a Service holds onto the objects is controlled by you. Particularly if CustomObject might be big, or the list might be long, I would rather you have greater control over how long that RAM is consumed.


If this is primarily to handle orientation changes, could Activity#onRetainNonConfigurationInstance() do what you want?

an activity can use this API to propagate extensive state from the old to new activity instance, from loaded bitmaps, to network connections, to evenly actively running threads. Note that you should not propagate any data that may change based on the configuration, including any data loaded from resources such as strings, layouts, or drawables.

This API won't help you if you're trying to do more than persist data across configuration changes.


As far as I know, SavedInstanceState is meant to save the UI configuration of the activity (like standard Android UI widgets, e.g. text field, are conserved automatically).

If you want to save a custom object between different activity relaunches (this doesn't apply to the user-initiated finishing of an activity by clicking the back button, but it does apply e.g. for orientation changes). Use the following code to retain an object:

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

And in the onCreate(Bundle savedInstanceState) method, you can then retrieve the object:

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


Why not just save the object to the SD Card? You can see an example of how I use this at my blog >> 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;
      }
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜