Чтение и запись массивов объектов Parcelable
У меня есть следующий класс, который считывает и записывает массив объектов из/в посылку:
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];
}
};
}
В приведенном выше коде я читаю ClassCastException
при чтении readParcelableArray
:
ОШИБКА/AndroidRuntime (5880): вызвано: java.lang.ClassCastException: [Landroid.os.Parcelable;
Что не так в коде выше? При написании массива объектов следует сначала преобразовать массив в ArrayList
?
UPDATE:
Можно ли преобразовать массив объектов в ArrayList
и добавить его в пакет? Например, при написании:
ArrayList<MyClass> tmpArrya = new ArrayList<MyClass>(mObjList.length);
for (int loopIndex=0;loopIndex != mObjList.length;loopIndex++) {
tmpArrya.add(mObjList[loopIndex]);
}
out.writeArray(tmpArrya.toArray());
При чтении:
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);
}
Но теперь я получаю a NullPointerException
. Правильно ли выше подход? Почему он бросает NPE?
Ответы
Ответ 1
Вам нужно написать массив с помощью метода Parcel.writeTypedArray()
и прочитать его с помощью метода Parcel.createTypedArray()
, например:
MyClass[] mObjList;
public void writeToParcel(Parcel out) {
out.writeTypedArray(mObjList, 0);
}
private void readFromParcel(Parcel in) {
mObjList = in.createTypedArray(MyClass.CREATOR);
}
Причина, по которой вам не следует использовать методы readParcelableArray()
/writeParcelableArray()
, заключается в том, что readParcelableArray()
действительно создает Parcelable[]
. Это означает, что вы не можете передать результат метода MyClass[]
. Вместо этого вам нужно создать массив MyClass
той же длины, что и результат, и скопировать каждый элемент из массива результатов в массив MyClass
.
Parcelable[] parcelableArray =
parcel.readParcelableArray(MyClass.class.getClassLoader());
MyClass[] resultArray = null;
if (parcelableArray != null) {
resultArray = Arrays.copyOf(parcelableArray, parcelableArray.length, MyClass[].class);
}
Ответ 2
ОШИБКА/AndroidRuntime (5880): вызвано: java.lang.ClassCastException: [Landroid.os.Parcelable;
В соответствии с API метод readParcelableArray возвращает Parcelable array (Parcelable []), который нельзя просто отличить от массива MyClass (MyClass [ ]).
Но теперь я получаю исключение Null Pointer.
Трудно сказать точную причину без подробной трассировки стека исключений.
Предположим, что MyClass правильно реализует Parcelable, так мы обычно делаем для сериализации/десериализации массива объектов:
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];
}
};
}
Надеюсь, что это поможет.
Вы также можете использовать следующие методы:
public void writeToParcel(Parcel out, int flags) {
out.writeTypedList(mObjList);
}
private ClassABC(Parcel in) {
mObjList = new ArrayList<ClassABC>();
in.readTypedList(mObjList, ClassABC.CREATOR);
}
Ответ 3
У меня была аналогичная проблема, и я решил ее таким образом.
Я определил вспомогательный метод в MyClass, чтобы преобразовать массив Parcelable в массив объектов MyClass:
public static MyClass[] toMyObjects(Parcelable[] parcelables) {
MyClass[] objects = new MyClass[parcelables.length];
System.arraycopy(parcelables, 0, objects, 0, parcelables.length);
return objects;
}
Всякий раз, когда мне нужно прочитать множество массивов объектов MyClass, например. от намерения:
MyClass[] objects = MyClass.toMyObjects(getIntent().getParcelableArrayExtra("objects"));
EDIT. Вот обновленная версия той же функции, которую я использую совсем недавно, чтобы избежать компиляции предупреждений:
public static MyClass[] toMyObjects(Parcelable[] parcelables) {
if (parcelables == null)
return null;
return Arrays.copyOf(parcelables, parcelables.length, MyClass[].class);
}
Ответ 4
Вам нужно написать массив с помощью метода Parcel.writeTypedArray()
и прочитать его с помощью метода Parcel.readTypedArray()
, например:
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);
}
Для списков вы можете сделать следующее:
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);
}