Устранение ошибок в приложении для Android с помощью настраиваемых разделяемых классов

В моем приложении для Android я получаю несколько неупорядочивающих ошибок, хотя я думаю, что сделал все необходимое для правильного сохранения и загрузки объектов через Parcelables. Можете ли вы сказать мне, что не так с моим кодом?

Ошибка 1:

java.lang.RuntimeException: Unable to start activity ComponentInfo
Caused by: java.lang.RuntimeException: Parcel android.os.Parcel@41279860: Unmarshalling unknown type code 6619241 at offset 1372
at android.os.Parcel.readValue(Parcel.java:1922)
at android.os.Parcel.readMapInternal(Parcel.java:2094)
at android.os.Bundle.unparcel(Bundle.java:223)
at android.os.Bundle.getParcelable(Bundle.java:1158)
at android.app.Activity.onCreate(Activity.java:860)
at my.app.package.PlayComputer.onCreate(PlayComputer.java:1012)
at android.app.Activity.performCreate(Activity.java:4465)

Строка 1012 в MyActivity — это вызов super.onCreate(savedInstanceState); в onCreate() группы Activity.

protected void onSaveInstanceState(Bundle savedInstanceState) {
    if (myObject == null) {
        savedInstanceState.putParcelable("myObject", null);
    }
    else {
        savedInstanceState.putParcelable("myObject", myObject);
    }
    savedInstanceState.putInt(...);
    savedInstanceState.putString(...);
    savedInstanceState.putBoolean(...);
    super.onSaveInstanceState(savedInstanceState);
}

myObject относится к классу MyObject, который имеет следующие методы:

public void writeToParcel(Parcel out, int flags) {
    out.writeIntArray(...);
    out.writeInt(...);
    out.writeStringArray(...);
    out.writeString(...);
    out.writeParcelableArray(..., flags);
}

public static final Parcelable.Creator<MyObject> CREATOR = new Parcelable.Creator<MyObject>() {
    public MyObject createFromParcel(Parcel in) {
        try {
            if (in == null) {
                return null;
            }
            else {
                return new MyObject(in);
            }
        }
        catch (Exception e) {
            return null;
        }
    }
    public MyObject[] newArray(int size) {
        return new MyObject[size];
    }
};

private MyObject(Parcel in) {
    in.readIntArray(...);
    ... = in.readInt();
    in.readStringArray(...);
    ... = in.readString();
    ... = (OtherObject[]) in.readParcelableArray(OtherObject.class.getClassLoader());
}

Ошибка 2:

java.lang.RuntimeException: Unable to start activity ComponentInfo
Caused by: android.os.BadParcelableException: ClassNotFoundException when unmarshalling:
at android.os.Parcel.readParcelable(Parcel.java:1971)
at android.os.Parcel.readValue(Parcel.java:1859)
at android.os.Parcel.readMapInternal(Parcel.java:2099)
at android.os.Bundle.unparcel(Bundle.java:223)
at android.os.Bundle.getParcelable(Bundle.java:1158)
at android.app.Activity.onCreate(Activity.java:905)
at my.app.package.PlayComputer.onCreate(SourceFile:1012)

Те же файлы и классы.

Ошибка 3:

java.lang.RuntimeException: Unable to start activity ComponentInfo
Caused by: java.lang.RuntimeException: Parcel android.os.Parcel@4051aff8: Unmarshalling unknown type code 7340149 at offset 1276
at android.os.Parcel.readValue(Parcel.java:1913)
at android.os.Parcel.readMapInternal(Parcel.java:2083)
at android.os.Bundle.unparcel(Bundle.java:208)
at android.os.Bundle.getParcelable(Bundle.java:1100)
at my.app.package.PlayComputer.onCreate(SourceFile:1111)

На этот раз вызывающая строка (1111) следующая:

myObject = (MyObject) savedInstanceState.getParcelable("myObject");

person caw    schedule 21.12.2012    source источник
comment
Является ли конструктор GameState(Parcel in) опечаткой, и вы хотели написать конструктор MyObject(Parcel in)?   -  person fedepaol    schedule 22.12.2012
comment
Вы отдельно создаете объекты массива в своем конструкторе? Методы readXXXArray() требуют передачи полностью инициализированного экземпляра массива. Вы можете использовать методы createXXXArray(), чтобы вернуть вам новый экземпляр этого массива.   -  person devunwired    schedule 22.12.2012
comment
Эти массивы, конечно, были объявлены ранее, но еще не обязательно инициализированы. Из документации я не вижу, что это предварительное условие: developer.android .com/reference/android/os/Parcel.html   -  person caw    schedule 22.12.2012
comment
Что ж, документация по Parcel и Parcelable очень скудная, так что я не вижу различий между readXXXArray() и writeXXXArray.   -  person caw    schedule 22.12.2012
comment
Является ли writeTypedArray(MyObject.CREATOR) рекомендуемым методом сохранения Parcelable? Как вы можете видеть выше, я использовал writeParcelableArray().   -  person caw    schedule 22.12.2012


Ответы (3)


В Android есть два разных загрузчика классов: загрузчик классов фреймворка (который знает, как загружать классы Android) и загрузчик классов APK (который знает, как загружать ваш код). Загрузчик классов APK имеет загрузчик классов фреймворка, установленный в качестве его родителя, что означает, что он также может загружать классы Android.

Ошибка № 2, вероятно, вызвана тем, что Bundle использует загрузчик классов фреймворка, поэтому он не знает о ваших классах. Я думаю, это может произойти, когда Android должен сохранить ваш Bundle, а затем восстановить его (например, при нехватке памяти в фоновом режиме). Вы можете исправить это, установив загрузчик классов APK в комплекте:

saveInstanceState.setClassLoader(getClass().getClassLoader());

Ошибка № 1 и № 3 более загадочны, возможно, вы записываете нулевые значения в writeToParcel()? Боюсь, Android это не очень нравится.

person alexanderblom    schedule 30.12.2012
comment
Спасибо! savedInstanceState.setClassLoader(getClass().getClassLoader()); выглядит хорошо, но я боюсь, что могу назвать это только для одного класса. Что делать, если мне нужно поместить объекты из более чем одного класса в этот Bundle? Для какого класса это назвать? - person caw; 31.12.2012
comment
Кроме того, с некоторыми изменениями кажется, что проблема решена: stackoverflow.com/questions/13997550/ Можете ли вы представить, что именно такой способ чтения объектов вызвал проблемы? - person caw; 31.12.2012
comment
Это будет работать для всех ваших классов, так как getClass().getClassLoader() извлечет загрузчик классов APK (который может загружать все ваши классы). При чтении с помощью readXXXArray() какие массивы вы передавали? Как узнать, что они имеют правильную длину? createTypedArray() и другие не будут записывать/читать информацию о типе, что означает, что загрузчики классов на самом деле не задействованы. - person alexanderblom; 31.12.2012
comment
Я не знал, что readXXXArray() не является подходящей функцией, так как документация по этой теме очень скудна. Таким образом, в основном всегда следует использовать createXXXArray(), если только не требуется массив определенной длины, верно? И writeTypedArray() тоже следует предпочесть writeParcelableArray(), не так ли? Поскольку мой класс реализует Parcelable, я могу использовать оба. - person caw; 31.12.2012
comment
Кстати, мне нужно установить загрузчик классов для Bundle только в onSaveInstanceState() (при сохранении) или также в onCreate() (при восстановлении)? - person caw; 31.12.2012
comment
Документация здесь ужасна, вместо этого я посмотрел источник. Да, readXXXArray() полезен, только если у вас есть массив фиксированного размера. Я думаю, что writeTypedArray() немного чище, да, я не совсем уверен, каково было их намерение с этими разными вызовами. Вам нужно только установить его при восстановлении, при сохранении у вас уже есть загруженные классы, поэтому их загрузчик классов известен. - person alexanderblom; 01.01.2013
comment
Я попробовал savedInstanceState.setClassLoader(getClass().getClassLoader()); в onCreate моего фрагмента, но это ничего не изменило. У вас есть идеи, почему это не работает? - person Julian Sievers; 25.07.2014

Перед чтением из пакета установите ClassLoader:

bundle.setClassLoader(getClass().getClassLoader());

Это сэкономило мое время!

person hovo888s    schedule 19.11.2016

Судя по всему, createFromParcel и newArray должны быть переопределены следующим образом:

public static final Parcelable.Creator<MyObject> CREATOR = new Parcelable.Creator<MyObject>() {
    @Override
    public MyObject createFromParcel(Parcel in) {
        MyObject myObj = new MyObject();
        myObj.intArray = in.readIntArray(...);
        myObj.intValue = in.readInt(...);
        // ....
        // IN THE SAME ORDER THAT IS WRITTEN OUT AS PER writeToParcel!
        //
        return myObj;
    }
    @Override
    public MyObject[] newArray(int size) {
        return new MyObject[size];
    }
};

Изменить:

Я забыл упомянуть, что для того, чтобы вышеизложенное работало, должен быть пустой конструктор!

public MyObject(){}
person t0mm13b    schedule 21.12.2012
comment
Благодарю вас! Разве это не именно то, что я делаю? Я просто использую конструктор MyObject(in), чтобы было понятнее. Ну ладно, может быть, вы этого не видели из-за моей опечатки в имени конструктора, которая сейчас исправлена. - person caw; 22.12.2012
comment
Единственное, о чем я могу думать, возможно, должен быть пустой конструктор, исправленный ответ... - person t0mm13b; 22.12.2012
comment
подтвердите, что вы используете @Override два метода describeContents() и writeToParcel() :) - person t0mm13b; 22.12.2012
comment
Я проверил, что я переопределяю эти методы, и да, я это делаю. Разве вы не видите, что ваше решение и мое не совсем рабочее решение абсолютно одинаковы? Я только что поместил код во вспомогательный метод под названием GameState(Parcel in) — точно так же, как в документации. - person caw; 22.12.2012
comment
Именно так я использую его в своих проектах и ​​у меня нет проблем с Parcelable - извините, если мой ответ вам не очень поможет. - person t0mm13b; 22.12.2012
comment
Нет проблем ;) Здорово, что это работает для вас, но поскольку нет никакой разницы с моим решением, это не очень помогает. Вероятно, проблема связана с чтением и записью массивов (объектов, подлежащих разделению), а у вас этого нет, поэтому ваш код работает на вас. - person caw; 22.12.2012
comment
Между прочим, вы использовали массивы для чтения/записи, очевидно, что-то глупое, что вы где-то делаете неправильно, чтобы это взорвалось вам в лицо, за исключением... попробуйте напечатать его/logcat, чтобы увидеть, куда он идет... от инициализации, в каждый метод, чтобы увидеть, где это на самом деле идет не так, - это поможет вам прибить его :) protip Я использую logcat.d(TAG, if (....){ *** ЗДЕСЬ *** }еще{}); в каждом потоке операторов, чтобы проверить! Просто используйте свое воображение, которое вам подходит ;) - person t0mm13b; 22.12.2012
comment
Марко В. Я предполагаю, что ваша проблема заключается не в «Мой объект», а в «Другом объекте». Вы пытались убрать сортировку/десортировку только этого атрибута? - person Stefan de Bruijn; 30.12.2012
comment
Я заменил все readXXXArray() на createXXXArray() сейчас. Более того, я читал массивы разделяемых объектов так: obj[] = (Obj[]) in.readParcelableArray(Obj.class.getClassLoader()) Теперь я читаю их так: obj[] = in.createTypedArray(...) С этими двумя изменениями проблема, похоже, решена. Могло ли это быть причиной этих проблем? - person caw; 31.12.2012