2013-08-08 7 views
39

Учитывая пользовательский класс org.example.app.MyClass implements Parcelable, я хочу написать List<MyClass> в посылку. Я сделал сортировочный с«BadParcelableException: ClassNotFoundException когда демаршаллизации <myclass>» при использовании метода Parcel.read, который имеет ClassLoader в качестве аргумента

List<MyClass> myclassList = ... 
parcel.writeList(myclassList); 

всякий раз, когда я пытаюсь маршализация класса с

List<MyClass> myclassList = new ArrayList<MyClass>(); 
parcel.readList(myclassList, null); 

есть "BadParcelableException: ClassNotFoundException when unmarshalling org.example.app.MyClass" исключения.

Что здесь не так? Почему класс не найден?

+1

Я получил эту ошибку в другом контексте - вызывая 'bundle.keySet()' на комплекте, содержащем Parcelable. При запуске отдельного тестового класса против рассматриваемого кода он прошел, но запуск всего набора тестов привел к «BadParcelableException». «Исправить» было сделать 'bundle.setClassloader (MyClass.class.getClassLoader()) перед' bundle.keySet() '. –

ответ

84

Не отключайте пользовательский класс, то есть тот, который предоставляется вашим приложением, а не каркасом Android, с загрузчиком класса framework, который используется, когда вы даете null как аргумент Classloader. Используйте класс приложения загрузчик:

parcel.readList(myclassList, getClass().getClassLoader()); 

Всякий раз, когда метод Parcel.read*() также имеет ClassLoader в качестве аргумента (например, Parcel.readList(List outVal, ClassLoader loader)), и вы хотите, чтобы прочитать класс приложения из Parcel, используйте класс приложения загрузчик, который можно получить с помощью getClass().getClassLoader() ,

Фон: Android поставляется с двумя загрузчиками классов: загрузчик системного класса, который может загружать все системные классы, но те, которые предоставляются вашим приложением. И загрузчик класса приложения, который установил загрузчик системного класса в качестве родительского и, следовательно, может загружать все классы. Если вы даете нулевой загрузчик классов, Parcel.readParcelableCreator(), will use the framework class loader, вызывая в ClassNotFoundException.

Благодаря alexanderblom для обеспечения the hint, которые ведут меня на правильном пути

16

Я считаю более правильной формой этого будет:

List<MyClass> myclassList = new ArrayList<MyClass>(); 
parcel.readList(myclassList, MyClass.class.getClassLoader()); 

Потому что здесь вы явно с помощью загрузчика классов для общего типа списка.

+6

Это в основном то же самое, что и мой ответ. Нет никакой разницы между 'MyClass.class' и' getClass() ', оба возвращают один и тот же объект класса, поскольку' getClass() 'вызывается в MyClass. Кроме того, вы можете использовать любой класс здесь, если это пользовательский класс, а не тот, который предоставляется инфраструктурой. – Flow

+6

Я не сказал, что вы были неправы, просто эта форма может быть более подходящей. Также учтите, что вы делаете дополнительный вызов метода 'getClass()', который не нужен, когда вы ссылаетесь на класс статически. –

3

я столкнулась с той же проблемой, и вместо parcleable я использовал связку

intent.setExtrasClassLoader(getClassLoader()); 
/* Send optional extras */ 
Bundle bundle = new Bundle(); 
bundle.putParcelable("object", mObject); 
bundle.putString("stringVal", stringVal); 

intent.putExtra("bundle",bundle); 

И в моем умысле класса я использовал это, чтобы получить значение:

Bundle oldBundle = intent.getBundleExtra("bundle"); 
ResultReceiver receiver = oldBundle.getParcelable("object"); 
String stringVal = oldBundle.getString("stringVal"); 
intent.setExtrasClassLoader(getClassLoader()); 

Надеется, что это будет полезно для некоторые.

0

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

Предположим, вы являетесь подклассом BottomNavigationView, и вы хотите добавить сохраненное состояние к сверхчеловеку в onSaveInstanceState().

Неправильная реализация Parcelable шаблонного (скопированный из другого класса или шаблона) будет выглядеть следующим образом:

static class State extends BaseSavedState { 

    Bundle stateBundle; 

    //incorrect as super state uses ClassLoaderCreator 
    public static final Creator<State> CREATOR = new Creator<State>() { 
     public State createFromParcel(Parcel in) { 
      return new State(in); 
     } 

     public State[] newArray(int size) { 
      return new State[size]; 
     } 
    }; 

    State(Parcel source) { 
     super(source); 
     this.stateBundle = source.readBundle(getClass().getClassLoader()); 
    } 

    State(Parcelable superState) { 
     super(superState); 
    } 

    @Override 
    public void writeToParcel(Parcel out, int flags) { 
     super.writeToParcel(out, flags); 
     out.writeBundle(stateBundle); 
    } 
} 

Это не будет работать, как сверхдержаву из BottomNavigationView требует загрузчик классов. Вместо этого вы должны тщательно осмотреть SavedState класс от BottomNavigationView и использовать правильный ClassLoaderCreator, а не Creator:

static class State extends AbsSavedState { 

    Bundle stateBundle; 

    public static final Creator<State> CREATOR = new ClassLoaderCreator<State>() { 
     public State createFromParcel(Parcel in, ClassLoader classLoader) { 
      return new State(in, classLoader); 
     } 

     @Override 
     public State createFromParcel(Parcel source) { 
      return new State(source, null); 
     } 

     public State[] newArray(int size) { 
      return new State[size]; 
     } 
    }; 

    State(Parcel source, ClassLoader classLoader) { 
     super(source, classLoader); 
     this.stateBundle = source.readBundle(classLoader); 
    } 

    State(Parcelable superState) { 
     super(superState); 
    } 

    @Override 
    public void writeToParcel(Parcel out, int flags) { 
     super.writeToParcel(out, flags); 
     out.writeBundle(stateBundle); 
    } 
} 

Обратите внимание, что расширение android.support.v4.view.AbsSavedState может быть лучшим выбором, чем BaseSavedState или android.view.AbsSavedState, так как это позволит вам пройти класс загрузчик суперкласс:

SavedState(Parcel source, ClassLoader classLoader) { 
    super(source, classLoader); //available in android.support.v4.view.AbsSavedState 
    this.stateBundle = source.readBundle(classLoader); 
}