Parcelable对象的循环引用

3
我已经搜索了很多关于如何传递类型为ArrayList的对象的信息,但是我没有找到有用的东西来解决我的问题。这是我的问题:
我有一个类Vertex(实现Parcelable接口),它有4个属性:名称(String)、minDistance(double)、previous(Vertex)和一个邻接点列表(Edge对象的ArrayList)。
我还有一个类Edge(实现Parcelable接口),它有3个属性:from(Vertex)、target(Vertex)和weight(double)。
在我的MainActivity中,我有一个顶点的ArrayList(每个顶点都有一个边缘的ArrayList),我想将它们传递到另一个活动中。但此时我遇到了StackOverflowError错误。
Vertex.java类:
   public class Vertex implements Comparable<Vertex>, Parcelable {
    public final String name;
    public List<Edge> adjacencies = new ArrayList<Edge>();
    public double minDistance = Double.POSITIVE_INFINITY;
    public Vertex previous;

    public Vertex(String argName) {
        name = argName;
    }

    public Vertex(Parcel in) {
        this.name = in.readString();
        this.minDistance=in.readDouble();
        Object[]aristas= in.readArray(Edge.class.getClassLoader());
        setAdjacencies(aristas);
    }

    public void addEdge(Edge e) {
        adjacencies.add(e);
    }

    public String toString() {
        return name;
    }

    public int compareTo(Vertex other) {
        return Double.compare(minDistance, other.minDistance);
    }

    @Override
    public int describeContents() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // TODO Auto-generated method stub
        dest.writeString(name);
        dest.writeDouble(minDistance);     
        Object[] edgesArray = adjacencies.toArray();
        dest.writeArray(edgesArray);

    }

    public static final Parcelable.Creator<Vertex> CREATOR = new Parcelable.Creator<Vertex>() {
        public Vertex createFromParcel(Parcel in) {
            return new Vertex(in);
        }

        public Vertex[] newArray(int size) {
            return new Vertex[size];
        }
    };

    public void setAdjacencies(Object[] edges) {
        for (Object edge : edges) {
            if (edge instanceof Edge) {
                adjacencies.add((Edge) edge);
            }
        }
    }

}

类 Edge.java

   public class Edge implements Parcelable {

    private Vertex from;
    private Vertex target;
    private double weigth;

    public Edge(Vertex from, Vertex target, double weight) {
        this.target = target;
        this.weigth = weight;
        this.from = from;
    }

    public Edge(Parcel in) {
        this.weigth=in.readDouble();
        this.target=(Vertex) in.readParcelable(getClass().getClassLoader());
        this.from=(Vertex) in.readParcelable(getClass().getClassLoader());  
    }

    public Vertex getTarget() {
        return target;
    }

    public void setTarget(Vertex target) {
        this.target = target;
    }

    public double getWeigth() {
        return weigth;
    }

    public void setWeigth(double weigth) {
        this.weigth = weigth;
    }

    public Vertex getFrom() {
        return from;
    }

    public void setFrom(Vertex from) {
        this.from = from;
    }

    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return getFrom().toString() + " -> " + weigth + " -> "
                + getTarget().toString();
    }

    @Override
    public int describeContents() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // TODO Auto-generated method stub
        dest.writeDouble(weigth);
        dest.writeParcelable( target,flags);
        dest.writeParcelable(from, flags);

    }

    public static final Parcelable.Creator<Edge> CREATOR = new Parcelable.Creator<Edge>() {
        public Edge createFromParcel(Parcel in) {
            return new Edge(in);
        }

        public Edge[] newArray(int size) {
            return new Edge[size];
        }
    };

}

非常感谢您的帮助。

谢谢。

我忘记展示我如何发送和接收可序列化对象:

发送(Fragment.java):

i.putParcelableArrayListExtra("LIST_VERTEX", ((MainActivity)getActivity()).getListaVertex());

接收(ResultActivity.java):

nodos = getIntent().getParcelableArrayListExtra("LIST_VERTEX");

注意:((MainActivity)getActivity()).getListaVertex() 是一个 Vertex 对象的 ArrayList。
LOGCAT:
11-23 01:52:25.384: E/AndroidRuntime(21939): FATAL EXCEPTION: main 11-23 01:52:25.384: E/AndroidRuntime(21939): java.lang.StackOverflowError 11-23 01:52:25.384: E/AndroidRuntime(21939): 在android.os.Parcel.writeString(Parcel.java:513)处发生错误。 11-23 01:52:25.384: E/AndroidRuntime(21939): 在com.csacanam.dijkstra.Vertex.writeToParcel(Vertex.java:56)处发生错误。 11-23 01:52:25.384: E/AndroidRuntime(21939): 在android.os.Parcel.writeParcelable(Parcel.java:1254)处发生错误。 11-23 01:52:25.384: E/AndroidRuntime(21939): 在com.csacanam.dijkstra.EdgesList.writeToParcel(EdgesList.java:48)处发生错误。 11-23 01:52:25.384: E/AndroidRuntime(21939): 在com.csacanam.dijkstra.Vertex.writeToParcel(Vertex.java:58)处发生错误。 发生堆栈溢出错误,错误源自于android.os.Parcel.writeString(Parcel.java:513)。在编写com.csacanam.dijkstra.Vertex.writeToParcel(Vertex.java:56)和com.csacanam.dijkstra.EdgesList.writeToParcel(EdgesList.java:48)时,调用了android.os.Parcel.writeParcelable(Parcel.java:1254),导致了错误。

编辑:

这个解决方法解决了我的循环引用问题

新的Edge类:

public class Edge implements Parcelable {

    public String PKhash;
    private Vertex from;
    private Vertex target;
    private double weigth;

    public static HashMap<String, Vertex> _parentsFrom = new HashMap<String, Vertex>();
    public static HashMap<String, Vertex> _parentsTarget = new HashMap<String, Vertex>();

    public String get_PKhash() {
        /* Return a unique identifier of your Edge object here */
        return PKhash;
    }

    public Edge(Vertex from, Vertex target, double weight) {
        this.target = target;
        this.weigth = weight;
        this.from = from;
        this.PKhash=from.name+","+getWeigth()+","+target.name;
    }

    public Edge( Parcel in ) {
        this.weigth = in.readDouble();
        this.PKhash  = in.readString();
        Assert.assertTrue( (from = _parentsFrom.get( this.PKhash )) != null );
        Assert.assertTrue( (target = _parentsTarget.get( this.PKhash )) != null );
    }


    public Vertex getTarget() {
        return target;
    }

    public void setTarget(Vertex target) {
        this.target = target;
    }

    public double getWeigth() {
        return weigth;
    }

    public void setWeigth(double weigth) {
        this.weigth = weigth;
    }

    public Vertex getFrom() {
        return from;
    }

    public void setFrom(Vertex from) {
        this.from = from;
    }

    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return getFrom().toString() + " -> " + weigth + " -> "
                + getTarget().toString();
    }

    @Override
    public int describeContents() {
        // TODO Auto-generated method stubz
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // TODO Auto-generated method stub
        dest.writeDouble(weigth);
        this.PKhash="";
        this.PKhash=from.name+","+getWeigth()+","+target.name;
        dest.writeString(PKhash);
        _parentsFrom.put(PKhash, from);
        _parentsTarget.put( PKhash, target);

    }

    public static final Parcelable.Creator<Edge> CREATOR = new Parcelable.Creator<Edge>() {
        public Edge createFromParcel(Parcel in) {
            return new Edge(in);
        }

        public Edge[] newArray(int size) {
            return new Edge[size];
        }
    };


}

新顶点类
public class Vertex implements Comparable<Vertex>, Parcelable {
    public final String name;
    public List<Edge> adjacencies = new ArrayList<Edge>();
    public double minDistance = Double.POSITIVE_INFINITY;
    public Vertex previous;

    public Vertex(String argName) {
        name = argName;
    }

    public Vertex(Parcel in) {
        this.name = in.readString();
        this.minDistance=in.readDouble();
        Object[]aristas= in.readArray(Edge.class.getClassLoader());
        setAdjacencies(aristas);
    }

    public void addEdge(Edge e) {
        adjacencies.add(e);
    }

    public String toString() {
        return name;
    }

    public int compareTo(Vertex other) {
        return Double.compare(minDistance, other.minDistance);
    }

    @Override
    public int describeContents() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // TODO Auto-generated method stub
        dest.writeString(name);
        dest.writeDouble(minDistance);     
        Object[] edgesArray = adjacencies.toArray();
        dest.writeArray(edgesArray);

    }

    public static final Parcelable.Creator<Vertex> CREATOR = new Parcelable.Creator<Vertex>() {
        public Vertex createFromParcel(Parcel in) {
            return new Vertex(in);
        }

        public Vertex[] newArray(int size) {
            return new Vertex[size];
        }
    };

    public void setAdjacencies(Object[] edges) {
        for (Object edge : edges) {
            if (edge instanceof Edge) {
                adjacencies.add((Edge) edge);
            }
        }
    }

    @Override
    public int hashCode() {
        // TODO Auto-generated method stub
        return name.hashCode();
    }

}

1
为什么需要实现Parcelable的ArrayList?你可以直接将Parcelable对象添加到ArrayList中。 - Gopal Gopi
那是我尝试解决问题的最后一件事。在那之前,我只是为每个顶点拥有一个ArrayList<Edge implements Parcelable>,但当我尝试发送ArrayList<Vertex>时,我遇到了相同的异常。 - Camilo Sacanamboy
请发布logcat日志 - Egor
为什么在Vertex类中使用EdgesList邻接表?我在那里找不到任何用处... - Gopal Gopi
我刚刚修改了它。现在我只有一个ArrayList<Edge>,但问题仍然存在。我将在帖子中修改这个更改。 - Camilo Sacanamboy
显示剩余2条评论
1个回答

1
您不需要在您的EdgeList中实现Parcelable。事实上,据我所知,您根本不需要EdgeList。只需按照以下方式修改Vertex类即可:
public List<Edge> adjacencies = new ArrayList<Edge>();

public Edge(Parcel in) {
    name = in.getString();
    minDistance = in.getDouble();
    adjancencies = in.readParcelableArray(Edge.getClass().getClassLoader()); 
}

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(name);
    dest.writeDouble(minDistance);
    pc.writeParcelableArray((ArrayList<? extends Parcelable>) adjacencies, flags);

}

我不确定你是否可以在Edge类中使用Vertex成员,因为这可能会创建一个循环引用。先尝试不使用这些成员进行测试。
编辑:
阅读了这里的一些建议:android parcelable referencing another parcelable circular dependence,我认为你可以尝试以下方法:
Edge类中,不要在writeToParcel()中写入对Vertex成员的引用。而是尝试以下方法:
public class Edge implements Parcelable {

    private Vertex from;
    private Vertex target;
    private double weigth;

    static HashMap<Long, Vertex> _parentsFrom = new HashMap<Long, Vertex>();
    static HashMap<Long, Vertex> _parentsTarget = new HashMap<Long, Vertex>();

    public long get_PKhash() { /* Return a unique identifier of your Edge object here */ }

    private ItemClass( Parcel in ) {
        (...)
        assertTrue( (from = _parentsFrom.remove( get_PKhash() )) != null );
        assertTrue( (target = _parentsTarget.remove( get_PKhash() )) != null );
    }

    public void writeToParcel( Parcel p, int args ) {
        (...)//Don't write to the parcel your from and target objects
        _parentsFrom.put( this.get_PKhash, from);
        _parentsTarget.put( this.get_PKhash, to);
    }
}   

注意,似乎在Parcelable实现中无法使用循环引用。上述解决方案是一种hackish的解决方法,只有在您可以确保每个边缘实例在应用程序中具有唯一标识时才能起作用。希望它有帮助。

谢谢你的回答,Ricardo。我刚刚更改了我的Vertex类,现在属性adjacencies是一个ArrayList<Edge>,但仍然存在同样的问题。我在没有Vertex成员的Edge类中进行了测试,但问题是我在其他活动中需要两个Vertex成员(来自和目标)。因此我遇到了NullPointerException。 - Camilo Sacanamboy
它不起作用,因为我需要在其他活动中使用那些对象。因此,我需要在writeToParcel中发送它们并在构造函数Vertex(Parcel in)中接收它们。 - Camilo Sacanamboy
你在谈论哪些对象? - Ricardo
我按照你说的更改了代码,但现在出现了AssertionErrorFailed错误。我打算在post中添加我的新Edge类。 - Camilo Sacanamboy
非常感谢。这个解决方法对我很有效。我只需要修改一些代码。我将在帖子中添加修改后的类(Vertex和Edge)。 - Camilo Sacanamboy
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接