实现包含其他可Parcelable对象的Parcelable时出现问题

7

我将实现一个包含另一个Parcelable对象的Parcelable类。

在OuterParcelable类中:

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

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

然后:
public OuterParcelable(Parcel parcel) {
    super();

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

并且:

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

当我使用上述代码重新创建对象时,会得到以下结果:
 08-18 17:13:08.566: ERROR/AndroidRuntime(15520): Caused by: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: my.package.InnerParcelable
2个回答

3

一种干净的方式将非原始属性存储为可包含,可能为空的值。 使用Parcel.writeValue()readValue()。 请参见下面代码中的注释:

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 ...    
}

非常好用。writeValue()和readValue()封装了处理可能为空值和类型检测的过程。从javadoc中可以看到:

public final void writeValue (Object v)
将一个通用对象展开到一个包裹中。给定的对象值目前可以是以下类型之一:
null、String、Integer等
String[]、boolean[]等
实现Parcelable协议的任何对象。...


2

你为什么要把值放入Bundle中?你的类是否完全实现了Parcelable接口?

Parcelable骨架

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];
    }
};

我添加了一些东西以便更方便地处理空值,但原理是相同的。你需要 @Override 项、构造函数和 Creator。

如果你要读写可包裹对象,如果指定了 null 作为类加载器,则会遇到问题。


1
我的问题是 - 想要将一个可包含的对象放入另一个可包含的对象中。结果发现这是不可能的。 - pixel
我经常这样做。实际上,上面的代码片段展示了在一个Parcelable中编写另一个Parcelable。 - Cameron Lowell Palmer
1
我遇到了和pixel一样的问题,通过仔细查看Cameron Lowell Palmer报告的骨架,我解决了这个问题;关键问题是我将一个空类加载器传递给读取内部可包含对象。谢谢Cameron,我投票支持你的答案。 - Giorgio Barchiesi

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