问题解组parcelables

Jer*_*gan 59 java android marshalling parcelable

我有一些实现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)
...
Run Code Online (Sandbox Code Playgroud)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

这是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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Class c = loader == null ? Class.forName(name) : Class.forName(name, true, loader);
Run Code Online (Sandbox Code Playgroud)

为什么找不到课程?它既存在又实现Parcelable.

Ogn*_*yan 83

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

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


And*_*ess 35

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

消息包含一个Bundle,我放置了我的Parcelable后代:

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

messenger.send(msg);
Run Code Online (Sandbox Code Playgroud)

当远程服务尝试取消选中时,我遇到了同样的异常.在我的情况下,我监督了远程服务确实是一个单独的操作系统进程.因此,我必须设置当前的类加载器以供服务端的取消选择过程使用:

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

DownloadFileURLItem urlItem = (DownloadFileURLItem)
bundle.getParcelable("DOWNLOADFILEURLITEM");
Run Code Online (Sandbox Code Playgroud)

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


mxc*_*xcl 9

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

in.readParceleable(getContext().getClassLoader());
Run Code Online (Sandbox Code Playgroud)

而不是:

in.readParceleable(null); 
Run Code Online (Sandbox Code Playgroud)

要么

in.readParceleable(MyClass.class.getClassLoader()); 
Run Code Online (Sandbox Code Playgroud)

  • 我想你的意思是LocationType.class.getClassLoader() (13认同)

Amo*_*tir 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();
    }
Run Code Online (Sandbox Code Playgroud)

顺便说一句,作者正确地做了这件事,但有一天它可能对某人有所帮助.

  • 帮帮我,谢谢!犯了一个愚蠢的错误,浪费了几个小时,因为这...错误消息并没有真正帮助太多. (2认同)

mka*_*ski 6

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

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

  • 我也解决了这个问题,这是我从自己的错误中学到的东西(以及来自Binder/Parcel/Parcelable的恶劣文档):1)你必须配对调用来写/读*确切*(即你不能使用writeValue()来写一个`Parcelable`和readParcelable()来读它.2)不要使用readTypedList(),它总是打破我.相反,createTypedArrayList()使用起来非常简单,并且也按预期工作.3)on*every*调用readXYZ,它需要一个`ClassLoader`,传递该类'loader,而不是null.当它必须在其内部映射中取消值时,readBundle()也是如此.HTH. (24认同)
  • 使用正确的`ClassLoader`修复了这个谢谢! (5认同)

小智 6

我也得到了ClassNotFoundException并且在这里发布了我的解决方案答案,这使我找到了正确的方向.我的情况是我有嵌套的parcelable对象.对象A包含Object B的ArrayList.两者都实现了Parcelable.在A类中编写B对象列表:

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

}
Run Code Online (Sandbox Code Playgroud)

阅读A类清单:

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

谢谢!


小智 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();
}
Run Code Online (Sandbox Code Playgroud)

  • 你如何处理位置为"null"的情况? (2认同)