【问题标题】:StackOverflowError while writing Parcelable编写 Parcelable 时出现 StackOverflowError
【发布时间】:2016-07-29 06:48:33
【问题描述】:

我有两个类,假设它们看起来像这样:

电影.java

public class Movie implements Parcelable { 
     private int year;
     private List<Actor> actors;

     // Constructor, getters and setters, Parcelable implementation
}

Actor.java

public class Actor implements Parcelable {
    private String name;
    private Movie movie;

    // Constructor, getters and setters, Parcelable implementation
}

现在,当我尝试将 Movie 写入 parcelable 时,我得到 StackOverflowError:

java.lang.StackOverflowError
    at java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1012)
    at java.util.concurrent.ConcurrentHashMap.putIfAbsent(ConcurrentHashMap.java:1535)
    at java.lang.ClassLoader.getClassLoadingLock(ClassLoader.java:463)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:404)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at android.os.Parcel.writeParcelable(Parcel.java)
    atcom.example.android.data.model.Actor.writeToParcel(Actor.java:58)
    at android.os.Parcel.writeTypedList(Parcel.java:1106)
    at com.example.android.data.model.Movie.writeToParcel(Movie.java:70)

我知道这是嵌套类的问题,因为当它尝试将 Movie 写入 Parcel 时,它会尝试写入 Actor,但在 Actor 中它会再次尝试写入 Movie。

问题

如何避免嵌套类的问题?

【问题讨论】:

标签: java android parcelable


【解决方案1】:

不要将Movie 存储在Actor 中。它们之间的双向关系正在产生错误。

此外,Actor 应该存储 Movie 甚至没有意义。现实生活中的演员可以出演很多电影,也可以没有,或者他们只演电视剧。

【讨论】:

  • 来吧,这是假设性的。我知道它正在制造错误,我写过它。如何处理这样的模型结构?
  • 这是父子关系。最好的方法是仅将子项存储在父项中,而不是相反。
【解决方案2】:

这将在将Movie 对象写入Parcelable 时创建一个无限循环,如果您确实需要从Actor 中引用Movie,您可以尝试向Movie 添加一个ID类并将此 ID 提供给Actor,稍后您可以使用此 ID 跟踪相应的Movie,尽管我不确定这是否仍符合您的要求。无论如何,在 OO 语言中强烈反对使用循环引用。

【讨论】:

    【解决方案3】:

    改为实现Externalizable。然后使用writeObject 序列化具有对象身份的帐户的引用。

    Java 序列化协议旨在处理对象之间的循环相互依赖关系。 Parcelables 不支持那些设计 - 试图破解这种支持会毫无意义地重复工作,ObjectInputStreamObjectOutputStream 的创建者已经完成了工作。请注意,我并不是建议实现 Serializable(它很慢,因为它是基于反射的),而是实现 Externalizable,它与 Parcelable 基本相同,只是它与序列化配合得很好。

    ObjectOutputStream 本身既不是 Serializable 也不是 Parcelable,但您可以将其定向到 ByteArrayOutputStream 并传递结果字节数组:

    public static byte[] serialize(Externalizable object) {
        final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    
        ObjectOutputStream objectStream = null;
        try {
            objectStream = new ObjectOutputStream(buffer);
            objectStream.writeObject(object);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (objectStream != null) {
                try { objectStream.close(); } catch (IOException ignored) {}
            }
        }
    
        return buffer.toByteArray();
    }
    
    public static <T extends Externalizable> T deserialize(byte[] bytes) {
        ObjectInputStream objectStream = null;
        try {
            objectStream = new ObjectInputStream(new ByteArrayInputStream(bytes));
            return (T) objectStream.readObject();
        } catch (IOException | ClassNotFoundException e) {
            throw new RuntimeException(e);
        } finally {
            if (objectStream != null) {
                try { objectStream.close(); } catch (IOException ignored) {}
            }
        }
    }
    

    这是你的课程现在的样子:

    演员:

    class Actor implements Externalizable {
      private String name;
      private Movie movie;
    
      public Actor(String name, Movie movie) {
        this.name = name;
        this.movie = movie;
      }
    
      // required by Externalizable contract
      public Actor() {
      }
    
      @Override
      public void readExternal(ObjectInput input) throws IOException, ClassNotFoundException {
        name = input.readUTF();
        movie = (Movie) input.readObject();
      }
    
      @Override
      public void writeExternal(ObjectOutput output) throws IOException {
        output.writeUTF(name);
        output.writeObject(movie);
      }
    
      ...
    }
    

    电影:

    class Movie implements Externalizable {
      private List<Actor> actors;
    
      private int year;
    
      public Movie(int year) {
        this.year = year;
    
        actors = new ArrayList<>();
      }
    
      public void addActors(Actor... actors) {
        Collections.addAll(this.actors, actors);
      }
    
      // required by Externalizable contract
      public Movie() {
      }
    
      @Override
      @SuppressWarnings("unchecked")
      public void readExternal(ObjectInput input) throws IOException, ClassNotFoundException {
        year = input.read();
        actors = (List<Actor>) input.readObject();
      }
    
      @Override
      public void writeExternal(ObjectOutput output) throws IOException {
        output.write(year);
        output.writeObject(actors);
      }
    
      ...
    }
    

    我刚刚在我的设备上进行了测试,并且能够通过 Intent 跨活动成功传递交叉引用电影/演员。

    【讨论】:

    • 抱歉,这是安卓系统。
    • 看来你还没有真正理解我的建议。我添加了一些示例代码,希望对您有所帮助。
    猜你喜欢
    • 2018-01-22
    • 2011-04-19
    • 2021-12-22
    • 2019-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-09
    相关资源
    最近更新 更多