Ich habe folgende Klasse, die ein Array von Objekten von / in ein Paket liest und schreibt:
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];
}
};
}
Im obigen Code bekomme ich ClassCastException
beim Lesen eine readParcelableArray
:
FEHLER / AndroidRuntime (5880): Auslöser: java.lang.ClassCastException: [Landroid.os.Parcelable;
Was ist im obigen Code falsch? Sollte ich beim Schreiben des Objektarrays zuerst das Array in ein konvertieren ArrayList
?
AKTUALISIEREN:
Ist es in Ordnung, ein Objektarray in ein zu konvertieren ArrayList
und es dem Paket hinzuzufügen? Zum Beispiel beim Schreiben:
ArrayList<MyClass> tmpArrya = new ArrayList<MyClass>(mObjList.length);
for (int loopIndex=0;loopIndex != mObjList.length;loopIndex++) {
tmpArrya.add(mObjList[loopIndex]);
}
out.writeArray(tmpArrya.toArray());
Beim Lesen:
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);
}
Aber jetzt bekomme ich eine NullPointerException
. Ist der obige Ansatz korrekt? Warum wirft es eine NPE?
android
parcelable
User7723337
quelle
quelle
Antworten:
Sie müssen das Array mit der
Parcel.writeTypedArray()
Methode schreiben und mit derParcel.createTypedArray()
Methode zurücklesen , wie folgt:MyClass[] mObjList; public void writeToParcel(Parcel out) { out.writeTypedArray(mObjList, 0); } private void readFromParcel(Parcel in) { mObjList = in.createTypedArray(MyClass.CREATOR); }
Der Grund, warum Sie die
readParcelableArray()
/writeParcelableArray()
-Methoden nicht verwenden sollten , ist, dass als ErgebnisreadParcelableArray()
wirklich ein erstellt wirdParcelable[]
. Dies bedeutet, dass Sie das Ergebnis der Methode nicht in umwandeln könnenMyClass[]
. Stattdessen müssen Sie einMyClass
Array mit der gleichen Länge wie das Ergebnis erstellen und jedes Element aus dem Ergebnisarray in dasMyClass
Array kopieren .Parcelable[] parcelableArray = parcel.readParcelableArray(MyClass.class.getClassLoader()); MyClass[] resultArray = null; if (parcelableArray != null) { resultArray = Arrays.copyOf(parcelableArray, parcelableArray.length, MyClass[].class); }
quelle
if (parcelableArray != null) {...}
vereinfacht werdenresultArray = Arrays.copyOf(parcelableArray, parcelableArray.length, MyClass[].class);
in.createTypedArray()
stattdessen verwenden. Bitte überprüfen Sie das aktualisierte Beispiel.Laut API gibt die readParcelableArray-Methode das Parcelable-Array (Parcelable []) zurück, das nicht einfach in das MyClass-Array (MyClass []) umgewandelt werden kann.
Ohne die detaillierte Ablaufverfolgung des Ausnahmestapels ist es schwierig, die genaue Ursache zu ermitteln.
Angenommen, Sie haben MyClass dazu gebracht, Parcelable ordnungsgemäß zu implementieren. So gehen wir normalerweise vor, um ein Array von Parcelable-Objekten zu serialisieren / deserialisieren:
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]; } }; }
Hoffe das hilft.
Sie können auch die folgenden Methoden verwenden:
public void writeToParcel(Parcel out, int flags) { out.writeTypedList(mObjList); } private ClassABC(Parcel in) { mObjList = new ArrayList<ClassABC>(); in.readTypedList(mObjList, ClassABC.CREATOR); }
quelle
MyClass.class.getClassLoader()
statt seingetClass().getClassLoader()
? Weil ich denke, dass der Klassenlader verwendet wird, um die Parceleable-Klasse zu finden, die wir lesen möchten.Ich hatte ein ähnliches Problem und löste es auf diese Weise. Ich habe in MyClass eine Hilfsmethode definiert, um ein Array von Parcelable in ein Array von MyClass-Objekten zu konvertieren:
public static MyClass[] toMyObjects(Parcelable[] parcelables) { MyClass[] objects = new MyClass[parcelables.length]; System.arraycopy(parcelables, 0, objects, 0, parcelables.length); return objects; }
Wann immer ich ein paketierbares Array von MyClass-Objekten lesen muss, z. B. aus einer Absicht:
MyClass[] objects = MyClass.toMyObjects(getIntent().getParcelableArrayExtra("objects"));
BEARBEITEN : Hier ist eine aktualisierte Version derselben Funktion, die ich in jüngerer Zeit verwende, um Kompilierungswarnungen zu vermeiden:
public static MyClass[] toMyObjects(Parcelable[] parcelables) { if (parcelables == null) return null; return Arrays.copyOf(parcelables, parcelables.length, MyClass[].class); }
quelle
Sie müssen das Array mit der
Parcel.writeTypedArray()
Methode schreiben und mit derParcel.readTypedArray()
Methode zurücklesen , wie folgt: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); }
Für Listen können Sie Folgendes tun:
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); }
quelle
MyClass
gibtinterface
? Ich bin mir nicht sicher, wie ichMyClass.CREATOR
in diesem Fall damit umgehen soll .MyClass
es sich um eine Schnittstelle handelt, müssen alle Implementierungen implementiert werdenParcelable
und über eigeneCREATOR
s verfügen .Set
einen Schnittstellentyp (nennen wir esMyInterface
), der implementiertParcelable
, aber keine konkretenCREATOR
s. Ich habe immerdest.writeTypedList(new ArrayList<>(mySet))
geschrieben undArrayList<MyInterface> data = (ArrayList<MyInterface>) in.readArrayList(MyInterface.class.getClassLoader());
gelesenParcel
. Nachteil: Es gibt eine hässliche Warnung für eine ungeprüfte Besetzung.CREATOR
. Aber mein Objekt, das paketiert wird, enthält einSet<MyInterface>
(einfach getipptMyInterface
). DasSet
kann enthaltenMyInterfaceA
,MyInterfaceB
usw. Solange mein Set eingegeben werden mussMyInterface
, kann ich eine nicht verwendenCREATOR
, oder bin ich falsch? Vielleicht haben Sie für mich ein Beispiel für dieses Szenario? Die Verwendung einer BaseClass für alle MyInterface mit einerCREATOR
Implementierung würde helfen, aber daher benötige ich eine neue Klasse nur, um dieses Paket zu aktivieren ... Vielen Dank!