development

Parcelable을 비 정렬 화하는 문제

big-blog 2020. 12. 10. 20:48
반응형

Parcelable을 비 정렬 화하는 문제


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의 소스 에서 가져온 다음 코드에서 실패하고 있음을 알 수 있습니다 .

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

클래스를 찾을 수없는 이유는 무엇입니까? 그것은 존재하고 구현 Parcelable합니다.


이것이 "답변"에서 대답되지 않았기 때문에 나는 대답을 게시 할 것입니다. @ Max-Gontar가 지적했듯이 올바른 ClassLoader를 얻고 ClassNotFound 예외를 제거하려면 LocationType.class.getClassLoader ()를 사용해야합니다. 즉 :

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


다음 설정에서 동일한 문제가 발생했습니다. 일부 핸들러는 메시지를 생성하고 메신저를 통해 원격 서비스로 보냅니다.

메시지에는 Parcelable 하위 항목을 넣은 번들이 포함되어 있습니다.

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

messenger.send(msg);

원격 서비스가 배송을 취소하려고 할 때 동일한 예외가 발생했습니다. 제 경우에는 원격 서비스가 실제로 별도의 OS 프로세스임을 감독했습니다. 따라서 서비스 측에서 unparcelling 프로세스에서 사용할 현재 클래스 로더를 설정해야했습니다.

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

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

Bundle.setClassLoader는 적절한 Parcelable 클래스를로드하는 데 사용되는 클래스 로더를 설정합니다. 원격 서비스에서는 현재 클래스 로더로 재설정해야합니다.


문제는 내 응용 프로그램 ClassLoader를 unmarshalling 함수에 전달하지 않는다는 것입니다.

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

대신 :

in.readParceleable(null); 

또는

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

여기에 2 센트 만 더 해주면 반나절 넘게 머리를 긁적 거리며 잃었 기 때문입니다. writes 메서드와 reads 메서드를 똑같은 순서로 배치하지 않으면이 오류가 발생할 수 있습니다. 예를 들어 다음은 잘못된 것입니다.

 @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();
    }

그건 그렇고 저자는 이것을 올바르게했지만 언젠가는 누군가를 도울 수 있습니다.


Parcelable에 익숙하지 않지만 Serialization과 같은 경우 인터페이스를 구현하는 개체를 작성하기위한 각 호출은 writeToParcel ()에 대한 재귀 호출을 발생시킵니다. 따라서 호출 스택에있는 무언가가 실패하거나 null 값을 쓰는 경우 호출을 시작한 클래스가 올바르게 구성되지 않을 수 있습니다.

다음을 시도하십시오. writeToParcel ()에 대한 첫 번째 호출에서 시작하는 모든 클래스를 통해 writeToParcel () 호출 스택을 추적하고 모든 값이 올바르게 전송되는지 확인하십시오.


나는 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());
}

감사합니다!


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

글쎄, 나는 똑같은 문제를 가지고 있었고 그것이 해결책이라고 부르는지 전혀 알지 못하는 매우 어리석은 방식으로 해결했습니다.

다른 활동에 전달하려는이 수업이 있다고 가정 해 보겠습니다.

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

문제가 해결 되었나요 ??? !!!!


List 객체 유형의 속성을 가진 Object가있는 경우 속성을 읽을 때 클래스 로더를 전달해야합니다. 예를 들면 다음과 같습니다.

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 인터페이스를 구현하는 것이 중요합니다.

참고 URL : https://stackoverflow.com/questions/1996294/problem-unmarshalling-parcelables

반응형