カスタムクラスorg.example.app.MyClass implements Parcelable
が与えられた場合、List<MyClass>
をパーセルに書き込みます。マーシャリングをしました
List<MyClass> myclassList = ...
parcel.writeList(myclassList);
クラスをアンマーシャルしようとするたびに
List<MyClass> myclassList = new ArrayList<MyClass>();
parcel.readList(myclassList, null);
"BadParcelableException: ClassNotFoundException when unmarshalling org.example.app.MyClass"
例外があります。
ここで何が間違っていますか?クラスが見つからないのはなぜですか?
ClassLoaderとしてnull
を指定したときに使用されるフレームワーククラスローダーで、カスタムクラス(Androidフレームワークではなく、アプリケーションによって提供されるクラス)をアンマーシャリングしないでください。引数:アプリケーションクラスローダーを使用します。
_parcel.readList(myclassList, getClass().getClassLoader());
_
Parcel.read*()
メソッドにも引数としてClassLoaderがあり(たとえば Parcel.readList(List outVal, ClassLoader loader)
)、Parcel
からアプリケーションクラスを読み取りたい場合、getClass().getClassLoader()
で取得できるアプリケーションクラスローダーを使用します。
Background:Androidには2つのクラスローダーが付属しています:システムクラスローダーは、すべてのシステムクラスをロードできますが、アプリケーションによって提供されるもの、およびシステムクラスローダーを親として設定し、すべてのクラスをロードできるアプリケーションクラスローダー。クラスローダーとしてnullを指定すると、 Parcel.readParcelableCreator()
フレームワーククラスローダーを使用します 、 ClassNotFoundException が発生します。
ヒント を提供してくれたalexanderblomに感謝します。
これのより正しい形式は次のようになると思います:
List<MyClass> myclassList = new ArrayList<MyClass>();
parcel.readList(myclassList, MyClass.class.getClassLoader());
ここでは、リストのジェネリック型に対してクラスローダーを明示的に使用しているためです。
私は同じ問題に直面しており、parcleableの代わりにbundleを使用しました
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());
それが一部の人に役立つことを願っています。
カスタムビューを誤ってサブクラス化すると、このエラーが発生する可能性があります。
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);
}