Чтение и запись массивов 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
?
ОБНОВИТЬ:
Можно ли преобразовать массив объектов в 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);
}
Но теперь я получаю NullPointerException
, Вышеуказанный подход верен? Почему он бросает NPE?
4 ответа
Вам нужно написать массив, используя 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);
}
ОШИБКА /AndroidRuntime(5880): вызвано: java.lang.ClassCastException: [Landroid.os.Parcelable;
Согласно API метод readParcelableArray возвращает массив Parcelable (Parcelable[]), который нельзя просто привести к массиву MyClass (MyClass[]).
Но теперь я получаю исключение Null Pointer.
Трудно сказать точную причину без подробной трассировки стека исключений.
Предположим, что вы сделали так, чтобы MyClass правильно реализовывал Parcelable, как мы обычно это делаем для сериализации / десериализации массива объектов 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);
}
У меня была похожая проблема, и я решил ее таким образом. Я определил вспомогательный метод в 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"));
РЕДАКТИРОВАТЬ: Вот обновленная версия той же функции, которую я использую совсем недавно, чтобы избежать предупреждений компиляции:
public static MyClass[] toMyObjects(Parcelable[] parcelables) {
if (parcelables == null)
return null;
return Arrays.copyOf(parcelables, parcelables.length, MyClass[].class);
}
Вам нужно написать массив, используя 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);
}