保存和恢复对象引用

时间:2018-01-09 00:10:38

标签: android

我知道我可以通过serializable和parcelable保存和恢复对象状态,但是如果这些对象相互引用会怎么样?他们的关系会恢复吗?

例如,如果我的活动有对象A和对象B,并且

对象A引用了对象B
对象B引用了对象A

在恢复对象A时,它将重新创建对象B的实例 在恢复对象B时,它将重新创建对象A的实例

因此,这种关系不再有效。他们不再相互引用。

如何保持这种关系,而不必在SQLite数据库中保留并链接它们?

1 个答案:

答案 0 :(得分:1)

A"正常" Parcelable接口的实现会将其所有字段写入包裹,包括其他对象(它们自己将字段写入包裹)。因此,通常,当您保存并恢复实例状态时,所有对象关系都将就位。

在需要保存/恢复的对象之间循环引用的特殊情况下,您仍然可以使用Parcelable,但您必须自定义写入/读取逻辑以解决循环引用。

考虑这个定义Parcelable类的最小活动,并尝试保存/恢复该类的两个实例,每个实例引用另一个:



public class MainActivity extends AppCompatActivity {

    private static final String KEY_FIRST = "MainActivity.KEY_FIRST";
    private static final String KEY_SECOND = "MainActivity.KEY_SECOND";

    private MyParcelable first;
    private MyParcelable second;

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putParcelable(KEY_FIRST, first);
        outState.putParcelable(KEY_SECOND, second);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (savedInstanceState != null) {
            first = savedInstanceState.getParcelable(KEY_FIRST);
            second = savedInstanceState.getParcelable(KEY_SECOND);
        }
        else {
            first = new MyParcelable("first name");
            second = new MyParcelable("second name");

            first.companion = second;
            second.companion = first;
        }
    }

    private static class MyParcelable implements Parcelable {

        public static final Creator<MyParcelable> CREATOR =
                new Creator<MyParcelable>() {

                    @Override
                    public MyParcelable createFromParcel(Parcel in) {
                        return new MyParcelable(in);
                    }

                    @Override
                    public MyParcelable[] newArray(int size) {
                        return new MyParcelable[size];
                    }
                };

        private String name;
        private MyParcelable companion;

        public MyParcelable(String name) {
            this.name = name;
            this.companion = null;
        }

        private MyParcelable(Parcel in) {
            this.name = in.readString();
            this.companion = in.readParcelable(MyParcelable.class.getClassLoader());
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(name);
            dest.writeParcelable(companion, flags);
        }

        @Override
        public int describeContents() {
            return 0;
        }
    }
}
&#13;
&#13;
&#13;

如果您运行此活动并旋转手机,您会发现两个MyParcelable对象已成功保存和恢复,包括彼此的引用。 然而,如果您使用应用后台,则会获得StackOverflowError

01-09 00:21:35.100 17941 17941 D Error   : ERR: exClass=java.lang.StackOverflowError
01-09 00:21:35.101 17941 17941 D Error   : ERR: exMsg=stack size 8MB
01-09 00:21:35.101 17941 17941 D Error   : ERR: file=AbstractStringBuilder.java
01-09 00:21:35.101 17941 17941 D Error   : ERR: class=java.lang.AbstractStringBuilder
01-09 00:21:35.101 17941 17941 D Error   : ERR: method=expandCapacity line=130
01-09 00:21:35.148 17941 17952 W art     : Suspending all threads took: 23.362ms
01-09 00:21:35.164 17941 17941 D Error   : ERR: stack=java.lang.StackOverflowError: stack size 8MB

这很有道理; outState.putParcelable(KEY_FIRST, first)将调用writeToParcel(),它将执行dest.writeParcelable(companion, flags),这将调用writeToParcel(),依此类推。

解决方案是使用我们在第一次构建对象时使用的相同模式来构建我们的保存和恢复:不要序列化(读取:写入包裹)&#34;伴侣&# 34;对象,并在恢复它们后手动将它们连接在一起:

&#13;
&#13;
public class MainActivity extends AppCompatActivity {

    private static final String KEY_FIRST = "MainActivity.KEY_FIRST";
    private static final String KEY_SECOND = "MainActivity.KEY_SECOND";

    private MyParcelable first;
    private MyParcelable second;

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putParcelable(KEY_FIRST, first);
        outState.putParcelable(KEY_SECOND, second);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (savedInstanceState != null) {
            first = savedInstanceState.getParcelable(KEY_FIRST);
            second = savedInstanceState.getParcelable(KEY_SECOND);
        }
        else {
            first = new MyParcelable("first name");
            second = new MyParcelable("second name");
        }

        first.companion = second;
        second.companion = first;
    }

    private static class MyParcelable implements Parcelable {

        public static final Creator<MyParcelable> CREATOR =
                new Creator<MyParcelable>() {

                    @Override
                    public MyParcelable createFromParcel(Parcel in) {
                        return new MyParcelable(in);
                    }

                    @Override
                    public MyParcelable[] newArray(int size) {
                        return new MyParcelable[size];
                    }
                };

        private String name;
        private MyParcelable companion;

        public MyParcelable(String name) {
            this.name = name;
            this.companion = null;
        }

        private MyParcelable(Parcel in) {
            this.name = in.readString();
            this.companion = null;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(name);
            // do not parcel the companion object
        }

        @Override
        public int describeContents() {
            return 0;
        }
    }
}
&#13;
&#13;
&#13;

请注意,如果您在此类的两个实例之间使用循环引用,则此解决方案可能会导致问题。毕竟,分配某些东西的正常行为是将其结构的全部写入包裹;在这里,我们故意省略某些东西以解决特殊情况。