Android Parcelable 引用另一个 Parcelable 循环依赖问题

8
相当简单的场景,但我在谷歌上找不到任何相关的内容,所以在这里提出:

class ContainerClass implements Parcelable {
  List<ItemClass> _items;
  (...)

  public void writeToParcel( Parcel p, int args ) {
    p.writeList( _items );
    (...)
  }
}

class ItemClass implements Parcelable {
  ContainerClass _containerRef;      
  (...)

  public void writeToParcel( Parcel p, int args ) {
    p.writeParcelable( _containerRef );
    (...)
  }      
}

这将不可避免地导致循环并溢出堆栈。

我的问题是:当我必须通过一个新的Activity传递上述类型的对象时,该如何处理。

(针对CommonsWare) Parcelable实现似乎确实没有检查和避免循环依赖关系。 带有类名替换为以上名称的堆栈跟踪:

08-12 10:17:45.233    5590-5590/com.package E/AndroidRuntime: FATAL EXCEPTION: main
        java.lang.StackOverflowError
        at com.package.ContainerClass.writeToParcel(ContainerClass.java:139)
        at android.os.Parcel.writeParcelable(Parcel.java:1254)
        at com.package.ItemClass.writeToParcel(ItemClass.java:182)
        at android.os.Parcel.writeParcelable(Parcel.java:1254)
        at android.os.Parcel.writeValue(Parcel.java:1173)
        at android.os.Parcel.writeList(Parcel.java:622)
        at com.package.ContainerClass.writeToParcel(ContainerClass.java:144)
        at android.os.Parcel.writeParcelable(Parcel.java:1254)
        at com.package.ItemClass.writeToParcel(ItemClass.java:182)
        at android.os.Parcel.writeParcelable(Parcel.java:1254)
        at android.os.Parcel.writeValue(Parcel.java:1173)
        at android.os.Parcel.writeList(Parcel.java:622)
2个回答

9

这将不可避免地导致循环并溢出堆栈。

据我所知,捆绑过程不处理 循环对象图形。我刚刚提交了 一个问题以获得更好的文档说明

一个解决方法是不要执行 p.writeParcelable( _containerRef );。相反,在您的 ContainerClass(Parcel in) 构造函数(或者您的 CREATOR 如何处理它),在读取完您的 _items 列表后,对该列表进行迭代,并告诉每个子项关于其父项的信息。


如果我必须通过ContainerClass传递参数,那么这种解决方法非常好。不幸的是,我必须传递一个ItemClass实例的集合到某个活动中。 虽然我可以反过来操作而不使用p.writeList,因为在程序的这个部分我并不真正需要那些信息,但是当"Parseled" ContainerClass实例没有_items时,该实现会在其他部分造成问题。 - Skod

1

我思考了一下,想到了两种解决方案,如果有人遇到同样的问题也可以用到:

1) (受CommonsWare启发) 在链的每个部分上放置一个标志以指示方向 向上的继承关系是有损的,因为无法恢复到ContainerClass中的所有项目。

class ContainerClass implements Parcelable {
  boolean _parcelableDownHeirarchy = true;
  List<ItemClass> _items;
  (...)

  private ContainerClass( Parcel in ) {
    _items = in.readArrayList( ItemClass.class.getClassLoader() );
    (...)

    if ( _parcelableDownHierarchy ) {
      for ( int i = 0; i < _items.size(); i++ ) 
        _items.get( i ).set_container( this );
    }          
  }

  public void writeToParcel( Parcel p, int args ) {
    p.writeByte( (byte)_parcelableDownHierarchy ? 1 : 0 );
    if ( _parcelableDownHeirarchy )
      p.writeList( _items );

    (...)
  }
}


class ItemClass implements Parcelable {
  boolean _parcelableDownHeirarchy = true;
  ContainerClass _containerRef;      
  (...)

  private ItemClass( Parcel in ) {
    if ( !_parcelableDownHeirarchy ) {
      _containerRef = in.readParcelable( ContainerClass.class.getClassLoader() );
      //Won't contain item in it's _items list.          
    }
  }

  public void writeToParcel( Parcel p, int args ) {
    p.writeByte( (byte)_parcelableDownHierarchy ? 1 : 0 );
    if ( !_parcelableDownHeirarchy ) //Up the heirarchy
      p.writeParcelable( _containerRef );

    (...)
  }      
}

2) 使用静态哈希表的Hackish解决方法,只要每个对象都可以通过其可包装属性得到唯一标识。(在我的情况下,我在对象中有主键在我的数据库中)

class ContainerClass implements Parcelable {
  //Leave be
}


class ItemClass implements Parcelable {
  HaspMap<Long, ContainerClass> _parents = new HashMap<Long, ContainerClass>();
  ContainerClass _containerRef;      
  (...)

  public long get_PKhash() { /* Return unique identifier */ }

  private ItemClass( Parcel in ) {
    (...)
    assertTrue( (_containerRef = _parents.remove( get_PKhash() )) != null );
  }

  public void writeToParcel( Parcel p, int args ) {
    (...)//Don't write _containerRef
    _parents.put( this.get_PKhash, _containerRef );
  }      
}

我最终实现了第二个。 - Skod

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