Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

android parcelable referencing another parcelable circular dependence

Rather simple scenario really, but I could not find anything related on Google so here goes:

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 );
    (...)
  }      
}

This will inevitably loop and overflow the stack.

My question: How am I supposed to deal with a situation where I have to pass an object of the above types through to a new Activity.

(For CommonsWare) Parcelable implementation does indeed not seem to check for, and avoid circular dependencies. Stacktrace with classnames replaced by above names:

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)
like image 611
Skod Avatar asked Aug 11 '13 21:08

Skod


2 Answers

This will inevitably loop and overflow the stack.

AFAIK, the parceling process does not handle circular object graphs. I just filed an issue to get this better documented.

One workaround is to not do p.writeParcelable( _containerRef );. Instead, in ContainerClass, in your ContainerClass(Parcel in) constructor (or however your CREATOR is handling it), after reading in your _items list, iterate over that list and tell each child about its parent.

like image 198
CommonsWare Avatar answered Nov 15 '22 23:11

CommonsWare


I've been pondering some more and come up with two workarounds useful if anyone else is in the same boat:

1) (Inspired by CommonsWare) Put a flag on each part of the chain to indicate direction Up the heirarchy is lossy in the sense that all items to the ContainerClass cannot be restored.

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 workaround, employing a static hash table, granted each object can be uniquely identified by it's parcelable attributes. (In my case I have the primary key in my database in the objects).

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 );
  }      
}
like image 41
Skod Avatar answered Nov 15 '22 22:11

Skod