61

我有一些实现Parcelable的类,其中一些类相互包含作为属性。我将这些课程编组为一个包裹,以便在活动之间传递它们。将它们编组到包裹工作正常,但是当我尝试解组它们时,我收到以下错误:

...
AndroidRuntime  E  Caused by: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: schemas.Arrivals.LocationType
AndroidRuntime  E   at android.os.Parcel.readParcelable(Parcel.java:1822)
AndroidRuntime  E   at schemas.Arrivals.LayoverType.<init>(LayoverType.java:121)
AndroidRuntime  E   at schemas.Arrivals.LayoverType.<init>(LayoverType.java:120)
AndroidRuntime  E   at schemas.Arrivals.LayoverType$1.createFromParcel(LayoverType.java:112)
AndroidRuntime  E   at schemas.Arrivals.LayoverType$1.createFromParcel(LayoverType.java:1)
AndroidRuntime  E   at android.os.Parcel.readTypedList(Parcel.java:1509)
AndroidRuntime  E   at schemas.Arrivals.BlockPositionType.<init>(BlockPositionType.java:244)
AndroidRuntime  E   at schemas.Arrivals.BlockPositionType.<init>(BlockPositionType.java:242)
AndroidRuntime  E   at schemas.Arrivals.BlockPositionType$1.createFromParcel(BlockPositionType.java:234)
AndroidRuntime  E   at schemas.Arrivals.BlockPositionType$1.createFromParcel(BlockPositionType.java:1)
...

LayoverType班级(失败的地方):

public class LayoverType implements Parcelable {    
    protected LocationType location;
    protected long start;
    protected long end;

    public LayoverType() {}

    public LocationType getLocation() {
        return location;
    }

    public void setLocation(LocationType value) {
        this.location = value;
    }

    public long getStart() {
        return start;
    }

    public void setStart(long value) {
        this.start = value;
    }

    public long getEnd() {
        return end;
    }

    public void setEnd(long value) {
        this.end = value;
    }


    // **********************************************
    //  for implementing Parcelable
    // **********************************************

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeParcelable(location, flags);
        dest.writeLong(start);
        dest.writeLong(end  );
    }

    public static final Parcelable.Creator<LayoverType> CREATOR = new Parcelable.Creator<LayoverType>() {
        public LayoverType createFromParcel(Parcel in) {
            return new LayoverType(in);
        }

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

    private LayoverType(Parcel dest) {
        location = (LocationType) dest.readParcelable(null);    // it's failing here
        start = dest.readLong();
        end   = dest.readLong();
    }
}

这是LocationType课程:

public class LocationType implements Parcelable {
    protected int locid;
    protected String desc;
    protected String dir;
    protected double lat;
    protected double lng;

    public LocationType() {}

    public int getLocid() {
        return locid;
    }

    public void setLocid(int value) {
        this.locid = value;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String value) {
        this.desc = value;
    }

    public String getDir() {
        return dir;
    }

    public void setDir(String value) {
        this.dir = value;
    }

    public double getLat() {
        return lat;
    }

    public void setLat(double value) {
        this.lat = value;
    }

    public double getLng() {
        return lng;
    }

    public void setLng(double value) {
        this.lng = value;
    }

    // **********************************************
    //  for implementing Parcelable
    // **********************************************


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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt   (locid);
        dest.writeString(desc );
        dest.writeString(dir  );
        dest.writeDouble(lat  );
        dest.writeDouble(lng  );
    }

    public static final Parcelable.Creator<LocationType> CREATOR = new Parcelable.Creator<LocationType>() {
        public LocationType createFromParcel(Parcel in) {
            return new LocationType(in);
        }

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

    private LocationType(Parcel dest) {
        locid = dest.readInt   ();
        desc  = dest.readString();
        dir   = dest.readString();
        lat   = dest.readDouble();
        lng   = dest.readDouble();
    }
}

更新 2:据我所知,它在以下代码中失败(来自Parcel 的源代码):

Class c = loader == null ? Class.forName(name) : Class.forName(name, true, loader);

为什么找不到类?它既存在又实现Parcelable

4

10 回答 10

83

因为这没有在“答案”中回答,但在评论中我会发布一个答案:正如@Max-Gontar 指出的那样,您应该使用 LocationType.class.getClassLoader() 来获得正确的 ClassLoader 并摆脱 ClassNotFound 异常,即:

in.readParceleable(LocationType.class.getClassLoader());

于 2011-02-06T18:25:30.597 回答
36

我在以下设置中遇到了同样的问题:某个处理程序创建了一条消息并通过 Messenger 将其发送到远程服务。

消息包含一个捆绑包,我将 Parcelable 后代放在其中:

final Message msg = Message.obtain(null, 0);
msg.getData().putParcelable("DOWNLOADFILEURLITEM", downloadFileURLItem);

messenger.send(msg);

当远程服务尝试拆包时,我遇到了同样的异常。就我而言,我已经看到远程服务确实是一个单独的操作系统进程。因此,我必须设置当前的类加载器以供服务端的解包过程使用:

final Bundle bundle = msg.getData();
bundle.setClassLoader(getClassLoader());

DownloadFileURLItem urlItem = (DownloadFileURLItem)
bundle.getParcelable("DOWNLOADFILEURLITEM");

Bundle.setClassLoader 设置用于加载适当的 Parcelable 类的类加载器。在远程服务中,您需要将其重置为当前的类加载器。

于 2010-09-27T18:41:19.937 回答
9

我发现问题是我没有将我的应用程序 ClassLoader 传递给解组函数:

in.readParceleable(getContext().getClassLoader());

而不是:

in.readParceleable(null); 

或者

in.readParceleable(MyClass.class.getClassLoader()); 
于 2010-03-03T12:22:00.820 回答
8

只是在这里加上我的 2 美分,因为我为此浪费了半天多的时间。如果您不以完全相同的顺序放置写入方法和读取方法,您可能会收到此错误。例如,以下将是错误的:

 @Override
    // Order: locid -> desc -> lat -> dir -> lng
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt   (locid);
        dest.writeString(desc);
        dest.writeDouble(lat);
        dest.writeString(dir);
        dest.writeDouble(lng);
    }
    // Order: locid -> desc -> dir -> lat -> lng
    private LocationType(Parcel dest) {
        locid = dest.readInt();
        desc  = dest.readString();
        dir   = dest.readString();
        lat   = dest.readDouble();
        lng   = dest.readDouble();
    }

顺便说一句,作者正确地做到了这一点,但有一天它可能会对某人有所帮助。

于 2011-11-09T09:16:35.630 回答
6

我对 Parcelable 不是很熟悉,但如果它类似于序列化,那么每次调用编写实现接口的对象都会导致递归调用 writeToParcel()。因此,如果调用堆栈中的某些内容失败或写入空值,则可能无法正确构造发起调用的类。

尝试:通过从第一次调用 writeToParcel() 开始的所有类跟踪 writeToParcel() 调用堆栈,并验证所有值是否正确发送。

于 2010-01-03T21:35:49.520 回答
6

我也得到了 ClassNotFoundException 并发布了我的解决方案,因为这里的答案让我朝着正确的方向前进。我的情况是我有嵌套的可打包对象。对象 A 包含对象 B 的 ArrayList。两者都实现 Parcelable。编写 A 类中 B 对象的列表:

@Override
public void writeToParcel(Parcel dest, int flags) {
    ...
    dest.writeList(getMyArrayList());

}

阅读 A 类的列表:

public ObjectA(Parcel source) {
    ...
    myArrayList= new ArrayList<B>();
    source.readList(myArrayList, B.class.getClassLoader());
}

谢谢!

于 2013-02-12T11:15:05.610 回答
5

而不是使用 writeParcelable 和 readParcelable 直接使用 writeToParcel 和 createFromParcel。所以更好的代码是:

@Override
public void writeToParcel(Parcel dest, int flags) {
    location.writeToParcel(dest, flags);
    dest.writeLong(start);
    dest.writeLong(end  );
}

public static final Parcelable.Creator<LayoverType> CREATOR = new Parcelable.Creator<LayoverType>() {
    public LayoverType createFromParcel(Parcel in) {
        return new LayoverType(in);
    }

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

private LayoverType(Parcel dest) {
    location = LocationType.CREATOR.createFromParcel(dest);
    start = dest.readLong();
    end   = dest.readLong();
}
于 2010-12-26T23:23:27.487 回答
2

好吧,我遇到了同样的问题,并以一种非常愚蠢的方式解决了它,我根本不知道它是否称为解决方案。

假设你有这门课要传给另一个活动

public class Person implements  Parcelable,Serializable{
public  String Name;
public  int Age;

@Override
public void writeToParcel(Parcel dest, int flags) {

 dest.writeString(name);
dest.writeInt(age);

}

public SeriesInfo(Parcel in) {

age= in.readInt(); //her was my problem as I have put age befor name
//while in the writeToParcel function I have defined
//dest.writeInt(age) after  in.readString();???!!!!
name= in.readString();

}
}

就是这样当我改变:

dest.writeString(name);
dest.writeInt(age);

dest.writeInt(age);
dest.writeString(name);

问题解决了???!!!

于 2016-01-05T02:59:45.990 回答
0

如果您有一个具有 List 对象类型属性的对象,您应该在读取该属性时传递类加载器,例如:

public class Mall implements Parcelable {

    public List<Outstanding> getOutstanding() {
        return outstanding;
    }

    public void setOutstanding(List<Outstanding> outstanding) {
        this.outstanding = outstanding;
    }        

    protected Mall(Parcel in) {
        outstanding = new ArrayList<Outstanding>();
        //this is the key, pass the class loader
        in.readList(outstanding, Outstanding.class.getClassLoader());
    }

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

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

    public static final Parcelable.Creator<Mall> CREATOR = new Parcelable.Creator<Mall>() {
        public Mall createFromParcel(Parcel in) {
            return new Mall(in);
        }

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

注意:Outstanding 类实现 Parceable 接口很重要。

于 2013-11-21T21:59:28.840 回答
0

我得到了这个异常,因为我缺少一个构造函数。所有实现 Parcelable 的类都必须这样做:

// add new constructor
@RequiresApi(Build.VERSION_CODES.N)
private LocationType(Parcel dest, ClassLoader loader) {
    super(dest, loader);
    locid = dest.readInt();
    desc  = dest.readString();
    dir   = dest.readString();
    lat   = dest.readDouble();
    lng   = dest.readDouble();
}

public static final Creator<LayoverType> CREATOR = new ClassLoaderCreator<LayoverType>() {

    // add createFromParcel method with ClassLoader
    @Override
    public LayoverType createFromParcel(Parcel in, ClassLoader loader)
    {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? new LayoverType(in, loader) : new LayoverType(in);
    }

    public LayoverType createFromParcel(Parcel in) {
        // call other createFromParcel method.    
        return createFromParcel(in, null);
    }

    public LayoverType[] newArray(int size) {
        return new LayoverType[size];
    }
};
于 2019-12-12T07:47:28.580 回答