写入Parcelable时出现StackOverflowError

时间:2016-04-08 05:34:33

标签: java android parcelable

我有两个课程,我们假设他们看起来像这样:

Movie.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。

问题

如何避免嵌套类的问题?

3 个答案:

答案 0 :(得分:0)

请勿将Movie存储在Actor内。它们之间的双向关系正在产生错误。

此外,Actor应存储Movie甚至没有意义。一个现实生活中的演员可以在很多电影中播放,也可以不播放,也可以只播放电视剧。

答案 1 :(得分:0)

改为实施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成功地跨活动传递交叉引用的电影/演员。

答案 2 :(得分:0)

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