74 votes

Lecture et écriture de tableaux d'objets colisables

J'ai la classe suivante qui lit et écrit un tableau d'objets depuis/vers un paquet :

class ClassABC extends Parcelable {
    MyClass[] mObjList;

    private void readFromParcel(Parcel in) {
        mObjList = (MyClass[]) in.readParcelableArray(
                com.myApp.MyClass.class.getClassLoader()));
    }

    public void writeToParcel(Parcel out, int arg1) {
        out.writeParcelableArray(mObjList, 0);
    }

    private ClassABC(Parcel in) {
        readFromParcel(in);
    }

    public int describeContents() {
        return 0;
    }

    public static final Parcelable.Creator<ClassABC> CREATOR =
            new Parcelable.Creator<ClassABC>() {

        public ClassABC createFromParcel(Parcel in) {
            return new ClassABC(in);
        }

        public ClassABC[] newArray(int size) {
            return new ClassABC[size];
        }
    };
}

Dans le code ci-dessus, j'obtiens un ClassCastException en lisant readParcelableArray :

ERROR/AndroidRuntime(5880) : Causé par : java.lang.ClassCastException : [Landroid.os.Parcelable ;

Quel est le problème dans le code ci-dessus ? Lors de l'écriture du tableau d'objets, dois-je d'abord convertir le tableau en un tableau de type ArrayList ?

UPDATE :

Est-il possible de convertir un tableau d'objets en un tableau d'objets ? ArrayList et l'ajouter au colis ? Par exemple, en écrivant :

    ArrayList<MyClass> tmpArrya = new ArrayList<MyClass>(mObjList.length);
    for (int loopIndex=0;loopIndex != mObjList.length;loopIndex++) {
        tmpArrya.add(mObjList[loopIndex]);
    }
    out.writeArray(tmpArrya.toArray());

En lisant :

    final ArrayList<MyClass> tmpList = 
            in.readArrayList(com.myApp.MyClass.class.getClassLoader());
    mObjList= new MyClass[tmpList.size()];
    for (int loopIndex=0;loopIndex != tmpList.size();loopIndex++) {
        mObjList[loopIndex] = tmpList.get(loopIndex);
    }

Mais maintenant je reçois un NullPointerException . L'approche ci-dessus est-elle correcte ? Pourquoi un NPE est-il lancé ?

152voto

Michael Points 16659

Vous devez écrire le tableau en utilisant l'option Parcel.writeTypedArray() et le relire avec la méthode Parcel.createTypedArray() comme suit :

MyClass[] mObjList;

public void writeToParcel(Parcel out) {
    out.writeTypedArray(mObjList, 0);
}

private void readFromParcel(Parcel in) {
    mObjList = in.createTypedArray(MyClass.CREATOR);
}

La raison pour laquelle vous ne devriez pas utiliser le readParcelableArray() / writeParcelableArray() méthodes est que readParcelableArray() crée réellement un Parcelable[] en conséquence. Cela signifie que vous ne pouvez pas convertir le résultat de la méthode en MyClass[] . Au lieu de cela, vous devez créer un MyClass de la même longueur que le résultat et copier chaque élément du tableau de résultat dans le tableau MyClass le tableau.

Parcelable[] parcelableArray =
        parcel.readParcelableArray(MyClass.class.getClassLoader());
MyClass[] resultArray = null;
if (parcelableArray != null) {
    resultArray = Arrays.copyOf(parcelableArray, parcelableArray.length, MyClass[].class);
}

36voto

yorkw Points 24676

ERROR/AndroidRuntime(5880) : Causé par : java.lang.ClassCastException : [Landroid.os.Parcelable ;

Selon le API La méthode readParcelableArray renvoie un tableau de Parcelable (Parcelable[]), qui ne peut pas être simplement casté en tableau de MyClass (MyClass[]).

Mais maintenant j'ai une exception de pointeur nul.

Il est difficile de déterminer la cause exacte sans la trace détaillée de la pile des exceptions.


Supposons que vous ayez fait en sorte que MyClass implémente correctement Parcelable, c'est ainsi que nous faisons habituellement pour sérialiser/désérialiser un tableau d'objets parcelables :

public class ClassABC implements Parcelable {

  private List<MyClass> mObjList; // MyClass should implement Parcelable properly

  // ==================== Parcelable ====================
  public int describeContents() {
    return 0;
  }

  public void writeToParcel(Parcel out, int flags) {
    out.writeList(mObjList);
  }

  private ClassABC(Parcel in) {
    mObjList = new ArrayList<MyClass>();
    in.readList(mObjList, getClass().getClassLoader());
   }

  public static final Parcelable.Creator<ClassABC> CREATOR = new Parcelable.Creator<ClassABC>() {
    public ClassABC createFromParcel(Parcel in) {
      return new ClassABC(in);
    }
    public ClassABC[] newArray(int size) {
      return new ClassABC[size];
    }
  };

}

J'espère que cela vous aidera.

Vous pouvez également utiliser les méthodes suivantes :

  public void writeToParcel(Parcel out, int flags) {
      out.writeTypedList(mObjList);
  }

  private ClassABC(Parcel in) {
      mObjList = new ArrayList<ClassABC>();
      in.readTypedList(mObjList, ClassABC.CREATOR);
  }

10voto

Giorgio Barchiesi Points 2023

J'ai eu un problème similaire, et je l'ai résolu de cette façon. J'ai défini une méthode d'aide dans MyClass, pour convertir un tableau de Parcelable en un tableau d'objets MyClass :

public static MyClass[] toMyObjects(Parcelable[] parcelables) {
    MyClass[] objects = new MyClass[parcelables.length];
    System.arraycopy(parcelables, 0, objects, 0, parcelables.length);
    return objects;
}

Chaque fois que j'ai besoin de lire un tableau parcellaire d'objets MyClass, par exemple à partir d'une intention :

MyClass[] objects = MyClass.toMyObjects(getIntent().getParcelableArrayExtra("objects"));

EDITAR : Voici une version mise à jour de la même fonction, que j'utilise plus récemment, pour éviter les avertissements de compilation :

public static MyClass[] toMyObjects(Parcelable[] parcelables) {
    if (parcelables == null)
        return null;
    return Arrays.copyOf(parcelables, parcelables.length, MyClass[].class);
}

5voto

Zhuinden Points 3074

Vous devez écrire le tableau en utilisant l'option Parcel.writeTypedArray() et le relire avec la méthode Parcel.readTypedArray() comme suit :

MyClass[] mObjArray;

public void writeToParcel(Parcel out, int flags) {
    out.writeInt(mObjArray.length);
    out.writeTypedArray(mObjArray, flags);
}

protected MyClass(Parcel in) {
    int size = in.readInt();
    mObjArray = new MyClass[size];
    in.readTypedArray(mObjArray, MyClass.CREATOR);
}

Pour les listes, vous pouvez procéder comme suit :

  ArrayList<MyClass> mObjList;

  public void writeToParcel(Parcel out, int flags) {
      out.writeTypedList(mObjList);
  }

  protected MyClass(Parcel in) {
      mObjList = new ArrayList<>(); //non-null reference is required
      in.readTypedList(mObjList, MyClass.CREATOR);
  }

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