8

I have an abstract class A which implements Parcelable.

I have a class B and a class C who both extend A. How can I make them parcelable?

Of cause I could chain it and provide a CREATOR both in A and B like suggested in many posts. But since I have other Objects who store the A-B-C classes and implement Parcelable themselfes, that approach seems not to be working because when I want to pass an ArrayList of A I would have to use the CREATOR in the typed list by

ArrayList<A> elements = new ArrayList<>();
in.readTypedList(elements , B.CREATOR); // B.CREATOR? C.CREATOR???

Which obviously makes no sense. So how can I properly make A Parcelable?

I.e I want to make this class Parcelable so I can refer to A in a common way.

A)

public abstract class A implements Parcelable {

    final String globalVar;

    public A(String globalVar) {
        this.globalVar = globalVar;
    }
}

B)

public class B extends A {

    String bVar;


    public B(String global, String bVar) {
        super(global);
        this.bVar = bVar;
    }

    private B(Parcel in) {
        super(in.readString());
        this.bVar = in.readString();
    }


    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeString(bVar);
    }
}

C)

public class C extends A {

    String cVar;


    public C(String global, String cVar) {
        super(global);
        this.cVar = cVar;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeString(cVar);
    }
}
Paul Woitaschek
  • 6,717
  • 5
  • 33
  • 52

2 Answers2

12

I used Vincent Mimoun-Prat's parcelable architecture from this post: Parcelable and inheritance in Android and ran into the same typed list problem about having to specify an impossible abstract CREATOR.

Searching in Parcel's JavaDoc I found the method writeList(List val) that internally uses writeValue(Object) method for each object of the list and therefore calling writeToParcel() from subclasses (B and C from an A list). To unmarshall the list use its counterpart readList (List outVal, ClassLoader loader) where A ClassLoader has to be passed or an android.os.BadParcelableException is thrown, at least in my case.

Class containing A elements list should be something like this:

public class AContainer implements Parcelable {
    //Other AContainer fields
    List<A> elements = new ArrayList<>();
    //Other AContainer fields

    static final Parcelable.Creator<AContainer> CREATOR = new Parcelable.Creator<AContainer>() {
        @Override
        public AContainer createFromParcel(Parcel source) {
            return new AContainer(source);
        }

        @Override
        public AContainer[] newArray(int size) {
            return new AContainer[size];
        }
    };

    public AContainer() {
    }

    protected AContainer(Parcel source) {
        //read other AContainer fields
        source.readList(elements, A.class.getClassLoader());
        //read other AContainer fields
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        //write other AContainer fields
        dest.writeList(elements);
        //write other AContainer fields
    }
}

Using these methods may be a bit slower than readTypedList() and writeTypedList() but specific data from B and C subclasses is also "parcelled" and not only the fields from A abstract superclass (It will be impossible being abstract). You recover the right instances from B and C.

Community
  • 1
  • 1
  • Life saving answer. First I couldn't find why it was crashing when unmarshalling, then I didn't know how to fix it. Thankfully your post made my 2 hour depression end up with a smile. Thanks! – Sufian Oct 27 '15 at 15:03
  • It's important to note that `elements` has to be initialized before `Parcel.readList()` method is called. – Henry Sep 25 '17 at 04:35
-2
public class A implements Parcelable{

       public A(){

       }

       public A(Parcel in){
            super(in);
           // read from parcel
           // number = in.readInt() etc
       }

       @Оverride
       public int describeContents(){
           return 0;
       }

       @Override
       public void writeToParcel(Parcel dest, int flags) {
           super.writeToParcel(out, flags);
          // write to parcel
          // out.writeInt
       }
       public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
           public Student createFromParcel(Parcel in) {
               return new A(in); 
           }

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

B and C must also implement Parcelable

user840754
  • 456
  • 3
  • 7
  • And how is the data of B and C written then? That would be a dead Parcelable doing nothing, or am I missing something? – Paul Woitaschek Feb 17 '15 at 17:54
  • `super.writeToParcel(out, flags)`; and `super(in)` in B and C will make sure that the data from A is read. B and C also need to implement Parcelable – user840754 Feb 17 '15 at 17:55
  • But at the problematic part, when not knowing the type. How can I do that? If I call the CREATOR of your class, B and C wouldn't be touched, right? – Paul Woitaschek Feb 17 '15 at 17:56