Чтение и запись массивов разборных объектов


у меня есть следующий класс, который считывает и записывает массив объектов из/в посылку:

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.ленг.ClassCastException: [Landroid.ОС.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 72

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.ленг.ClassCastException: [Landroid.ОС.Parcelable;

По словам API, метод readParcelableArray возвращает Parcelable array (Parcelable[]), который не может быть просто приведен к массиву MyClass (MyClass[]).

но теперь я получаю исключение нулевого указателя.

трудно сказать точную причину без детального стека исключений след.


предположим, что вы сделали 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;
}

всякий раз, когда мне нужно прочитать parcelable массив объектов 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);
}

вам нужно написать массив с помощью 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);
  }