Android Parcel、Parcelableに関連するエラーにかなりはまったので書いておきます。
FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity ComponentInfo{hogehoge.HogeActivity}: java.lang.RuntimeException: Parcel android.os.Parcel@24940f: Unmarshalling unknown type code 7602259 at offset 2164
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3663)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3820)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2218)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:8001)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1039)
Caused by: java.lang.RuntimeException: Parcel android.os.Parcel@24940f: Unmarshalling unknown type code 7602259 at offset 2164
at android.os.Parcel.readValue(Parcel.java:3306)
at android.os.Parcel.readArrayMapInternal(Parcel.java:3624)
at android.os.BaseBundle.initializeFromParcelLocked(BaseBundle.java:292)
at android.os.BaseBundle.unparcel(BaseBundle.java:236)
at android.os.BaseBundle.getBoolean(BaseBundle.java:898)
上記のようなエラーが出てしまいます。
細かい説明は割愛しますが、例えばAndroidの画面をまたいで複雑なオブジェクトをやり取りしたい時に、このParcel、Parcelableという仕組みを使います。
Parcelableというインターフェースをimplementsすると、そのクラスがParcelとして扱えるようになります。
例えば次のように作るのですが
public class Hoge implements Parcelable {
private final int id;
private final String name;
public Hoge(int id, String name) {
this.id = id;
this.name = name;
}
protected Hoge(Parcel in) {
id = in.readInt(); // ★この順番と
name = in.readString();
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(id); // ★ここの順番は一緒でなくてはならない
dest.writeString(name);
}
public static final Creator<Hoge> CREATOR = new Creator<Hoge>() {
@Override
public Hoge createFromParcel(Parcel in) {
if(in.readInt() == 1) {
return new Hoge2(in);
}
return new Hoge(in);
}
@Override
public Hoge[] newArray(int size) {
return new Hoge[size];
}
};
@Override
public int describeContents() {
return 0;
}
}
Parcel inが引数のコンストラクタがParcelを読みだしてコンストラクトします。
writeToParcel()というメソッドで、Parcelに書き出しをします。
あんましここのイメージわきにくかったんですが、BlackHatのYoutubeさんにちょうどよい画像を見つけました。
下記からの引用になります。ありがとうございます。
https://www.youtube.com/watch?v=qIzMKfOmIAA&list=LL&index=1
こういう構造だから、読みだしと書き込みが一緒じゃないとダメなんですね。
絵にするとめちゃわかりやすいです!( ˊᵕˋ )
サンプルコードではこの★がついたところが読みだしと書き出しにあたります。
冒頭のエラー はこれが順番通りじゃないと読出しに失敗して起こるエラーだそうです。
何度も目をこすりながら確認しても読出しと書き出しは一緒。
ChatGPTに聞いてもわかりませんでした。
結局、理由はこの部分↓にありました。
public static final Creator<Hoge> CREATOR = new Creator<Hoge>() {
@Override
public Hoge createFromParcel(Parcel in) {
if(in.readInt() == 1) { // 💀 問題はここだった
return new Hoge2(in);
}
return new Hoge(in);
}
@Override
public Hoge[] newArray(int size) {
return new Hoge[size];
}
};
in.readInt() == 1 で、in の1つ目が使われている、ということみたいです。
この部分をほかに移すことでなんとかなりました。
Creatorの中も注意!ということです。