61 votes

Problème de désarchivage des colis

J'ai quelques classes qui implémentent Parcelable et certaines de ces classes se contiennent mutuellement comme propriétés. Je rassemble les classes dans un fichier Parcelle pour les transmettre entre les activités. Les transférer vers le colis fonctionne bien, mais lorsque j'essaie de les retirer, j'obtiens l'erreur suivante :

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

El LayoverType (où il échoue) :

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

Voici le LocationType classe :

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

Mise à jour 2 : Pour autant que je puisse dire, il échoue au niveau du bout de code suivant (tiré de Source de la parcelle ):

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

Pourquoi ne parvient-il pas à trouver la classe ? Elle existe et implémente à la fois Parcelable .

0 votes

Pourriez-vous afficher le code de la classe LocationType ? Il se peut que la méthode d'écriture de cette classe ne soit pas correcte d'une certaine manière.

0 votes

@MpKamowski : J'ai ajouté les bits Parcelable de LocationType. Les seuls autres éléments dans les deux classes sont juste des getters/setters.

0 votes

Est-il possible que le paquet dont LocationType fait partie ne soit pas visible dans le code où il est désarchivé ?

83voto

Ogre_BGR Points 4799

Comme cette question n'a pas été traitée dans la "réponse" mais dans un commentaire, je vais y répondre : Comme @Max-Gontar l'a indiqué, vous devriez utiliser LocationType.class.getClassLoader() pour obtenir le bon ClassLoader et vous débarrasser de l'exception ClassNotFound :

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

1 votes

C'était tellement utile ! Dans mon cas, j'ai dû passer le chargeur de classe lors du désarchivage d'un objet Parcelable qui contenait un bundle contenant également des instances du même type d'objet. Ex. public class MyObject implements Parcelable, Serializable { protected Bundle mBundle ; MyObject(Parcel parcel) { mBundle = parcel.readBundle(MyObject.class.getClassLoader()) ; } }

36voto

Andre Steingress Points 1689

J'ai eu le même problème avec la configuration suivante : un gestionnaire crée un message et l'envoie par Messenger à un service distant.

le Message contient une liasse où je mets mon descendant colisable :

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

messenger.send(msg);

J'ai eu la même exception lorsque le service à distance a essayé de se déconnecter. Dans mon cas, j'avais vérifié que le service distant était bien un processus os séparé. Par conséquent, j'ai dû définir le classloader actuel pour qu'il soit utilisé par le processus de décomposition du côté du service :

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

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

Bundle.setClassLoader définit le chargeur de classes qui est utilisé pour charger les classes Parcelable appropriées. Dans un service distant, vous devez le réinitialiser au chargeur de classe actuel.

9voto

mxcl Points 5921

J'ai découvert que le problème était que je ne passais pas mon ClassLoader d'application à la fonction de désarchivage :

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

Plutôt que :

in.readParceleable(null); 

OU

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

15 votes

Je suppose que vous voulez dire LocationType.class.getClassLoader()

0 votes

Cela semblait prometteur, mais ne résout pas mon problème. Pouvez-vous afficher le projet minimal qui illustre le problème et sa solution ? Je me débats avec l'exemple de code de Google et il se bloque dans l'activité du récepteur.

1 votes

@MaxGontar oui probablement. Mais ça fait tellement longtemps que j'ai écrit ça que je ne veux pas l'éditer au cas où ce serait faux.

6voto

mkamowski Points 329

Je ne suis pas très familier avec Parcelable mais si c'est quelque chose comme Serialization, chaque appel pour écrire un objet qui implémente l'interface provoquera un appel récursif à writeToParcel(). Par conséquent, si quelque chose le long de la pile d'appels échoue ou écrit une valeur nulle, la classe qui a initié l'appel peut ne pas être construite correctement.

Essayez : Tracez la pile d'appels writeToParcel() dans toutes les classes en commençant par le premier appel à writeToParcel() et vérifiez que toutes les valeurs sont envoyées correctement.

0 votes

J'ai eu des problèmes avec les NullPointers avant, mais j'ai initialisé toutes les valeurs et cela a disparu. Maintenant, il semble qu'il ne puisse pas trouver la classe.

0 votes

Oh, il semble que vous aviez raison depuis le début. D'après ce que je peux dire, ça a foiré parce que quelque chose n'a pas été initialisé et quand il a essayé de lire le code suivant Parcellable qu'il obtenait un null (ou quelque chose comme ça).

0 votes

Pouvez-vous nous dire ce que vous avez réparé exactement ? J'ai le même problème et je ne vois pas ce qui cloche dans mon code.

5voto

tannewt Points 51

Au lieu d'utiliser writeParcelable et readParcelable, utilisez directement writeToParcel et createFromParcel. Donc le meilleur code est :

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

2 votes

Comment géreriez-vous le cas où l'emplacement est null ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X