web-dev-qa-db-ja.com

区画化解除の問題

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

Update 2:私が知る限り、次のコードで失敗していると言えます( Parcelのソース ):

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

クラスが見つからないのはなぜですか?存在し、Parcelableを実装します。

58
Jeremy Logan

これは「答え」では答えられなかったが、コメントでは答えを投稿します:@ Max-Gontarが指摘したように、LocationType.class.getClassLoader()を使用して正しいClassLoaderを取得し、ClassNotFound例外を取り除く必要があります、すなわち:

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

82
Ognyan

次のセットアップでも同じ問題が発生しました。一部のハンドラーがメッセージを作成し、メッセンジャーを介してリモートサービスに送信します。

メッセージには、Parcelableの子孫を配置するバンドルが含まれています。

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

messenger.send(msg);

リモートサービスが小包を開こうとしたときにも同じ例外がありました。私の場合、リモートサービスは実際には別のosプロセスであると監督していました。したがって、サービス側のパーセル解除プロセスで使用される現在のクラスローダーを設定する必要がありました。

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

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

Bundle.setClassLoaderは、適切なParcelableクラスをロードするために使用されるクラスローダーを設定します。リモートサービスでは、現在のクラスローダーにリセットする必要があります。

34

問題は、アプリケーションのClassLoaderをアンマーシャリング関数に渡していないことでした。

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

のではなく:

in.readParceleable(null); 

OR

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

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

ちなみに、著者はこれを正しく行いましたが、いつか誰かを助けるかもしれません。

8

私はParcelableにあまり馴染みがありませんが、Serializationのようなものである場合、インターフェイスを実装するオブジェクトを記述する各呼び出しは、writeToParcel()の再帰呼び出しを引き起こします。したがって、呼び出しスタックに沿った何かが失敗したり、null値を書き込んだりすると、呼び出しを開始したクラスが正しく構築されない場合があります。

試してください:writeToParcel()の最初の呼び出しから始まるすべてのクラスを介してwriteToParcel()呼び出しスタックをトレースし、すべての値が正しく送信されていることを確認します。

6
mkamowski

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

ありがとうございました!

6
Camino2007

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

まあ、私は同じ問題を抱えており、非常に愚かな方法でそれを解決しましたが、それがまったく解決策と呼ばれたかどうかはわかりません。

別のアクティビティに渡したいこのクラスがあるとしましょう

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

問題は解決されました??? !!!!

2
Ahmad Moussa

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インターフェースを実装することが重要です。

0
shontauro