代码之家  ›  专栏  ›  技术社区  ›  Jeremy Logan

解封Parcelables时出现问题

  •  55
  • Jeremy Logan  · 技术社区  · 15 年前

    我有几个类可以实现 Parcelable 其中一些类作为属性彼此包含。我正在将类编组为 Parcel 在活动之间传递它们。把它们编组到包裹上很好,但是当我试图将它们取消编组时,我会得到以下错误:

    ...
    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's source ):

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

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

    9 回复  |  直到 8 年前
        1
  •  80
  •   Ognyan    13 年前

    因为这不是在“答案”中回答,而是在评论中,我将发布一个答案: 正如@max gontar指出的那样,您应该使用locationType.class.getClassLoader()来获取正确的类加载器并消除ClassNotFound异常,即:

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

        2
  •  34
  •   Andre Steingress    14 年前

    以下设置也有同样的问题:某个处理程序创建了一条消息,并通过信使将其发送到远程服务。

    消息包含一个包,我把我的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类的类加载器。在远程服务中,需要将其重置为当前类加载器。

        3
  •  9
  •   sud007    8 年前

    我发现问题是我没有将应用程序类加载器传递给解组函数:

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

    而不是:

    in.readParceleable(null); 
    

    in.readParceleable(MyClass.class.getClassLoader()); 
    
        4
  •  8
  •   Amokrane Chentir    13 年前

    只需在这里加上我的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();
        }
    

    顺便说一句,作者做得对,但总有一天会有所帮助。

        5
  •  6
  •   mkamowski    15 年前

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

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

        6
  •  6
  •   Camino2007    11 年前

    我也得到了ClassNotFoundException,并将我的解决方案发布到BC,这里的答案引导我找到了正确的方向。我的场景是我已经嵌套了Parcelable对象。对象A包含对象B的数组列表。两者都实现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());
    }
    

    谢谢您!

        7
  •  5
  •   tannewt    14 年前

    不要使用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();
    }
    
        8
  •  1
  •   Ahmad Moussa    9 年前

    我也有同样的问题,用一种非常愚蠢的方式来解决它,我不知道它是否叫做解决方案。

    假设您有这个类要传递给另一个活动

    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);
    

    问题解决了吗????!!!!!!

        9
  •  0
  •   klefevre    9 年前

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

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

    注意:杰出的类实现可分配接口是很重要的。