2013-08-08 7 views
39

Data una classe personalizzata org.example.app.MyClass implements Parcelable, voglio scrivere un List<MyClass> ad un pacco. Ho fatto il marshalling con"BadParcelableException: ClassNotFoundException quando unmarshalling <myclass>", mentre utilizzando il metodo Parcel.read che ha un ClassLoader come argomento

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

ogni volta che provo a unmarshall la classe con

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

c'è un'eccezione "BadParcelableException: ClassNotFoundException when unmarshalling org.example.app.MyClass".

Cosa c'è di sbagliato qui? Perché la classe non è stata trovata?

+1

Ho ricevuto questo errore in un contesto diverso - chiamando 'bundle.keySet()' su un pacchetto che conteneva un Parcelable. Durante l'esecuzione di una singola classe di test rispetto al codice in questione, è passata, ma l'esecuzione dell'intera suite di test ha portato a 'BadParcelableException'. La "correzione" era di fare 'bundle.setClassloader (MyClass.class.getClassLoader())' prima di 'bundle.keySet()'. –

risposta

84

Non eseguire l'unmarshall di una classe personalizzata, ad esempio quella fornita dall'applicazione e non dal framework Android, con il programma di caricamento classe framework utilizzato quando si fornisce null come argomento Classloader. Utilizzare il caricatore di classe di applicazione:

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

Ogni volta che un metodo Parcel.read*() ha anche un ClassLoader come argomento (ad esempio Parcel.readList(List outVal, ClassLoader loader)) e si desidera leggere una classe di applicazione da un Parcel, utilizzare il caricatore classe di applicazione che possono essere recuperati con getClass().getClassLoader() .

Sfondo: Android viene fornito con due programmi di caricamento classe: il caricatore classe di sistema, che è in grado di caricare tutte le classi di sistema ma quelle fornite dall'applicazione. E il caricatore di classe dell'applicazione, che ha impostato il caricatore di classe di sistema come padre e quindi è in grado di caricare tutte le classi. Se assegni null un caricatore di classe, Parcel.readParcelableCreator(), will use the framework class loader, causando nello ClassNotFoundException.

Grazie a alexanderblom per fornire the hint che mi portano sulla strada giusta

16

credo che una più corretta forma di questo sarebbe:

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

Perché qui si sta esplicitamente utilizzando il caricatore di classe per il tipo generico della lista.

+6

È praticamente la stessa della mia risposta. Non c'è differenza tra 'MyClass.class' e' getClass() ', entrambi restituiscono lo stesso oggetto di classe poiché' getClass() 'è chiamato in MyClass. Inoltre, puoi usare qualsiasi classe qui, purché sia ​​una classe personalizzata e non una fornita dal framework. – Flow

+6

Non ho detto che non eri corretto, solo che questo modulo potrebbe essere più appropriato. Considera anche che stai facendo una chiamata di metodo addizionale a 'getClass()' che non è necessaria quando fai riferimento alla classe in modo statico. –

3

Ho affrontato lo stesso problema e invece di parcleable Ho usato fascio

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

intent.putExtra("bundle",bundle); 

E nella mia classe intento ho usato questo per recuperare il valore:

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

Spero che sarà utile per alcuni.

0

È possibile ottenere questo errore se si sottoclasse una vista personalizzata in modo errato.

Si supponga di essere in sottoclasse BottomNavigationView e si desidera aggiungere lo stato salvato al superstato in onSaveInstanceState().

Un'implementazione non corretta della boilerplate Parcelable (copiato da un'altra classe o un modello) sarebbe simile a questa:

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

Questo non avrebbe funzionato come il superstato da BottomNavigationView richiede un classloader. Invece si dovrebbe controllare attentamente la classe SavedState da BottomNavigationView e utilizzare il corretto ClassLoaderCreator piuttosto che 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); 
    } 
} 

Nota che l'estensione android.support.v4.view.AbsSavedState può essere una scelta migliore di BaseSavedState o android.view.AbsSavedState in quanto vi permetterà di passare una classe loader per la superclasse:

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