7

I'm implementing Parcelable class that has another Parcelable insde.

In OuterParcelable class:

@Override
public void writeToParcel(Parcel dest, int flags) {
    Bundle tmp = new Bundle();

    tmp.putParcelable("innerParcelable", mParcelable);
    dest.writeBundle(tmp);

and then:

public OuterParcelable(Parcel parcel) {
    super();

    Bundle b = parcel.readBundle();
    mParcelable = b.getParcelable("innerParcelable");

and:

    public OuterParcelable createFromParcel(Parcel in) {
        return new OuterParcelable(in);
    }

When I recreate object using above code I get:

 08-18 17:13:08.566: ERROR/AndroidRuntime(15520): Caused by: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: my.package.InnerParcelable
pixel
  • 24,905
  • 36
  • 149
  • 251

2 Answers2

3

A clean way to store non-primitive attributes as parcelable, possibly null, values. Use Parcel.writeValue() and readValue(). See comments in code below:

public class MyParcelableClass implements Parcelable {
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeValue(getIntegerAttribute()); // getIntegerAttribute() returns Integer
        dest.writeValue(getDoubleAttribute());
        dest.writeValue(getMyEnumAttribute()); // getMyEnumAttribute() returns a user defined enum
        dest.wrtieValue(getUserClassAttribute()); //UserClass must implement Parcelable in a similar fashion
    }

    private MyParcelableClass(Parcel in) {
        setIntegerAttribute((Integer)in.readValue(null)); //pass null to use default class loader. Ok for Integer, String, etc.
        setDoubleAttribute((Double)in.readValue(null)); //Cast to your specific attribute type
        setEnumAttribute((MyEnum)in.readValue(null));
        setUserClassAttribute((UserClass)in.readValue(UserClass.class.getClassLoader())); //Use specific class loader
    }

    @Override
    public int describeContents() ...

    public static final Parcelable.Creator<ParcelableLocationBean> CREATOR ...    
}

Works like a charm. writeValue() and readValue() encapsulate the dealing with possible nulls and type detection. From javadoc:

public final void writeValue (Object v)
Flatten a generic object in to a parcel. The given Object value may currently be one of the following types:
null, String, Integer, ...
String[], boolean[], ...
Any object that implements the Parcelable protocol. ...

Cléssio Mendes
  • 996
  • 1
  • 9
  • 25
2

Why are you putting the value into a Bundle? Did you completely implement the parcelable in your class?

Parcelable Skeleton

public MyClass(Parcel in) {
    readFromParcel(in);
}

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

@Override
public void writeToParcel(Parcel dest, int flags) { 
    dest.writeParcelable(aParcelableClass, flags);
}

private void writeObject(Parcel dest, Object obj) {
    if (obj != null) {
        dest.writeInt(1);
        dest.writeValue(obj);
    } else {
        dest.writeInt(0);
    }
}

public void readFromParcel(Parcel in) {
    aParcelableClass = in.readParcelable(ParcelableClass.class.getClassLoader());
}

private Object readObject(Parcel in) {
    Object value = null;

    if (in.readInt() == 1) {
        value = in.readValue(null); // default classloader
    }

    return value;
}

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

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

I added a few things to make null values more easily dealt with, but the principle is the same. You need the @Override items, constructor, and Creator.

If you're going to read and write a parcelable you will have issues if you specify null as the class loader.

Cameron Lowell Palmer
  • 21,528
  • 7
  • 125
  • 126
  • 1
    My problem was - wanted to put One parcelable into another. And it turned out that it wasn't possible. – pixel Aug 02 '12 at 12:58
  • I do it all the time. In fact the above code snippet shows writing a parcelable within a parcelable. – Cameron Lowell Palmer Aug 02 '12 at 13:24
  • 1
    I was having the same problem as pixel, I solved it by accurately looking at the skeleton reported by Cameron Lowell Palmer; the critical issue was that I was passing a null class loader to read the inner parcelable object. Thanks Cameron, I voted up your answer. – Giorgio Barchiesi Nov 04 '12 at 08:50