2013-08-08 9 views
39

Given niestandardowej klasy org.example.app.MyClass implements Parcelable, chcę napisać List<MyClass> do działki. Zrobiłem rozrządowych z„BadParcelableException: ClassNotFoundException gdy unmarshalling <myclass>” podczas korzystania z metody Parcel.read który ma ClassLoader jako argumentu

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

gdy próbuję wycofać klasę z

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

istnieje wyjątek "BadParcelableException: ClassNotFoundException when unmarshalling org.example.app.MyClass".

Co jest nie tak? Dlaczego nie znaleziono klasy?

+1

Dostałem ten błąd w innym kontekście - wywołanie 'bundle.keySet()' w pakiecie zawierającym paczkę. Po uruchomieniu indywidualnej klasy testowej względem kodu, o którym mowa, minęło, ale uruchomienie całego zestawu testów spowodowało 'BadParcelableException'. "Naprawiono" było zrobienie 'bundle.setClassloader (MyClass.class.getClassLoader())' przed 'bundle.keySet()'. –

Odpowiedz

84

Nie wolno odsyłać niestandardowej klasy, tj. Udostępnionej przez aplikację, a nie przez platformę Android, za pomocą programu ładującego klasy szkieletu, który jest używany, gdy podajesz null jako argument Classloader. Użyj programu ładującego klasy aplikacji:

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

Ilekroć metoda Parcel.read*() posiada również ClassLoader jako argumentu (np Parcel.readList(List outVal, ClassLoader loader)) i chcesz przeczytać klasę aplikacji z Parcel, użyj programu ładującego klasy aplikacji, które mogą być pobierane z getClass().getClassLoader() .

Tło: Android pochodzi z dwóch ładowarek klasy: klasa Loader System, który jest w stanie załadować wszystkich klas systemowych ale te przewidziane przez aplikację. I program ładujący klasy aplikacji, który ustawił system ładujący klasy jako nadrzędny i dlatego jest w stanie załadować wszystkie klasy. Jeśli podasz wartość null program ładujący klasy, Parcel.readParcelableCreator(), will use the framework class loader, powodując w ClassNotFoundException.

Dzięki alexanderblom za dostarczenie the hint które prowadzą mnie na właściwe tory

16

wierzę bardziej poprawna forma to byłoby:

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

Bo tu są jawnie przy użyciu programu ładującego klasy dla ogólnego typu Listy.

+6

To w zasadzie to samo, co moja odpowiedź. Nie ma różnicy między 'MyClass.class' a' getClass() ', oba zwracają ten sam obiekt klasy, ponieważ' getClass() 'jest wywoływany w MyClass. Możesz także użyć dowolnej klasy, o ile jest to klasa niestandardowa, a nie klasa dostarczana przez framework. – Flow

+6

Nie powiedziałem, że jesteś niepoprawny, tylko że ten formularz może być bardziej odpowiedni. Weź również pod uwagę, że robisz dodatkowe wywołanie metody do 'getClass()', które nie jest konieczne, gdy odwołujesz się do klasy statycznie. –

3

Mam wobec tego samego problemu i zamiast parcleable użyłem pakiet

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

intent.putExtra("bundle",bundle); 

I w moim zamiarem klasy Użyłem tego, aby pobrać wartość:

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

nadzieję, że będzie pomocny dla trochę.

0

Ten błąd można uzyskać, jeśli niepoprawnie podklasuje się niestandardowy widok.

Załóż, że podklasy są BottomNavigationView i chcesz dodać zapisany stan do superpaństwa w onSaveInstanceState().

Niewłaściwa realizacja Parcelable boilerplate (skopiowany z innej klasy lub szablonu) będzie wyglądać następująco:

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); 
    } 
} 

To nie będzie działać jako superpaństwa z BottomNavigationView wymaga classloader. Zamiast tego należy starannie kontrolować klasę SavedState z BottomNavigationView i użyć poprawne ClassLoaderCreator zamiast 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); 
    } 
} 

Zauważ, że rozszerzenie android.support.v4.view.AbsSavedState może być lepszym wyborem niż BaseSavedState lub android.view.AbsSavedState ponieważ pozwoli Ci zdać ładującego klasy do nadklasa:

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