【问题标题】:Save custom object array in instance state将自定义对象数组保存为实例状态
【发布时间】:2011-03-14 11:24:16
【问题描述】:

我有一个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 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)

有没有办法将自定义对象存储在实例状态中?

【问题讨论】:

    标签: java android serialization


    【解决方案1】:

    让你的 CustomObject 实现 Parcelable 并使用:

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

    还可以查看this 教程。

    编辑在 CommonsWare 评论后:

    如果您的 CustomObject 没有实现 SerializableParcelable 我会尝试将其包装在您自己的对象中并添加:

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

    【讨论】:

    • Felix 表示无法修改CustomObject
    • 我将此标记为已接受,因为这是我走的路线。但是,我与该库的作者进行了交谈,他们将实施它。
    【解决方案2】:

    让您的List&lt;CustomObject&gt; 由服务持有,并通过本地绑定模式让您的活动可以访问它。

    您不仅不必担心在您的实例状态中保留它,而且您可以更好地控制这些对象在内存中的生命周期。实例状态生命周期由 Android 控制; Service 持有对象的时间由您控制。特别是如果 CustomObject 可能很大,或者列表可能很长,我希望您能够更好地控制 RAM 的消耗时间。

    【讨论】:

    • 你的意思是Service 一直在启动并且不会停止?
    • 当有 1+ 个活动在运行时它不会停止。一旦没有更多的绑定连接,它将关闭。如果您愿意,您也可以使用自定义的 Application 对象 - 您失去控制但没有 Service 撒谎。
    • @CommonsWare:我猜他想Serialize 保存以备后用。使用这种方法,如果手机重新启动,他将丢失信息。
    • @Macarse:Felix 正试图让他的对象与onSaveInstanceState() 一起工作。我建议他简单地避免使用这些对象onSaveInstanceState()。他是否将Serializable 用于其他目的(例如,某种“保存”操作)与我的建议无关。在任何情况下,onSaveInstanceState() 都不是持久性机制。
    • 感谢您的想法,但我认为这会有点麻烦。此外,它还需要 大量 重构我的应用程序。
    【解决方案3】:

    如果这主要是为了处理方向变化,Activity#onRetainNonConfigurationInstance() 可以做你想做的吗?

    活动可以使用此 API 将广泛的状态从旧活动实例传播到新活动实例,从加载的位图到网络连接,再到均匀活跃的运行线程。请注意,您不应传播任何可能因配置而更改的数据,包括从字符串、布局或可绘制对象等资源加载的任何数据。

    如果您试图做的不仅仅是跨配置更改保留数据,此 API 将无济于事。

    【讨论】:

    • +1,我知道onRetainNonConfigurationInstance(),但不,它主要不是为了改变方向。
    【解决方案4】:

    据我所知,SavedInstanceState 旨在保存 Activity 的 UI 配置(如标准的 Android UI 小部件,例如文本字段,会自动保存)。

    如果您想在不同的活动重新启动之间保存自定义对象(这不适用于通过单击后退按钮由用户启动的活动完成,但它确实适用于例如方向更改)。使用以下代码保留对象:

    // 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's 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;
          }
    

    【讨论】:

    • 因为...我的对象是自定义的,无法序列化以便存储在 SD 卡上?
    猜你喜欢
    • 2011-03-14
    • 1970-01-01
    • 1970-01-01
    • 2012-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-29
    • 1970-01-01
    相关资源
    最近更新 更多