Activityを保持しないを有効にした時にCustomViewでの状態保存がうまく動作しない
Activityにボタンがあり、そのボタンを押すとギャラリーが開いて画像の読み込みをします。
この時、画面回転を行ってもギャラリーから選択した画像が保持されるようにしようとしています。
以下のコードでギャラリーから画像を選択すると、選択した画像がUriImageViewに表示されます。その後で画面回転を行うと選択した画像が保持されていました。
しかし、開発者オプションで「Activityを保持しない」オプションを有効にすると、以下のエラーが発生してアプリが落ちます。
エラー
04-26 21:30:43.793 22479-22479/? E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: jp.gcreate.sample.savestatecustomview, PID: 22479
java.lang.RuntimeException: Unable to start activity ComponentInfo{jp.gcreate.sample.savestatecustomview/jp.gcreate.sample.savestatecustomview.MainActivity2Activity}: java.lang.RuntimeException: Parcel android.os.Parcel@18c09797: Unmarshalling unknown type code 2131296303 at offset 264
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2325)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
at android.app.ActivityThread.access$800(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
Caused by: java.lang.RuntimeException: Parcel android.os.Parcel@18c09797: Unmarshalling unknown type code 2131296303 at offset 264
at android.os.Parcel.readValue(Parcel.java:2228)
at android.os.Parcel.readSparseArrayInternal(Parcel.java:2546)
at android.os.Parcel.readSparseArray(Parcel.java:1874)
at android.os.Parcel.readValue(Parcel.java:2209)
at android.os.Parcel.readArrayMapInternal(Parcel.java:2485)
at android.os.BaseBundle.unparcel(BaseBundle.java:221)
at android.os.Bundle.getSparseParcelableArray(Bundle.java:822)
at com.android.internal.policy.impl.PhoneWindow.restoreHierarchyState(PhoneWindow.java:2006)
at android.app.Activity.onRestoreInstanceState(Activity.java:1023)
at android.app.Activity.performRestoreInstanceState(Activity.java:978)
at android.app.Instrumentation.callActivityOnRestoreInstanceState(Instrumentation.java:1162)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2298)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
at android.app.ActivityThread.access$800(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
デバッグで確認すると、ギャラリーで画像を選択した後にActivityのonCreateで渡されるBundleの値がBundle[mParcelledData.dataSize=760]
となっていました。
CustomViewのonSaveInstanceStateがうまく動作していないのかと思い、UriImageViewのonSaveInstanceStateにブレークポイントを置いてステップ実行を行って確認してみました。
Bundleの値はBundle[{android:viewHierarchyState=Bundle[{android:views={16908290=android.view.AbsSavedState$1@33173215, 2131296291=jp.gcreate.sample.savestatecustomview.UriImageView$ImageState@8c95d9e, 2131296303=android.view.AbsSavedState$1@33173215, 2131296304=android.view.AbsSavedState$1@33173215, 2131296305=android.support.v7.widget.Toolbar$SavedState@303f5002, 2131296306=android.view.AbsSavedState$1@33173215, 2131296320=android.view.AbsSavedState$1@33173215}}]}]
となっており、アプリは落ちることなく動作しました。
ステップ実行するとうまく動作する理由がよく分からず困惑しています。
そもそもどこかのコードが根本的に間違っているのかもしれません。
どうやれば「Activityを保持しない」が有効な状態でも、カスタムビューの状態を保存できるでしょうか?
どこを直したら上手く動くようになるのか手助けしていただけるとありがたいです。
Activity
public class MainActivity2Activity extends ActionBarActivity {
private Button mButton;
private UriImageView mImageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_activity2);
mButton = (Button) findViewById(R.id.button);
mImageView = (UriImageView) findViewById(R.id.image);
}
@Override
public void onResume() {
super.onResume();
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_GET_CONTENT)
.setType("image/*");
startActivityForResult(intent, 1);
}
});
}
@Override
public void onPause() {
super.onPause();
mButton.setOnClickListener(null);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == 1 && resultCode == Activity.RESULT_OK){
Uri uri = data.getData();
mImageView.setUri(uri);
}
}
}
UriImageView
public class UriImageView extends ImageView{
private Uri mUri;
public UriImageView(Context context, AttributeSet attrs) {
super(context, attrs);
setImage();
}
private void setImage() {
if(mUri == null){
setImageDrawable(getContext().getResources().getDrawable(android.R.drawable.btn_star, getContext().getTheme()));
}else{
setImageURI(mUri);
}
}
public void setUri(Uri uri) {
mUri = uri;
setImage();
}
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
ImageState imageState = new ImageState(superState);
imageState.savedUri = mUri;
return imageState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
ImageState imageState = (ImageState) state;
super.onRestoreInstanceState(imageState.getSuperState());
setUri(imageState.savedUri);
requestLayout();
}
static class ImageState extends BaseSavedState{
public static final Parcelable.Creator<ImageState> CREATER = new Parcelable.Creator<ImageState>(){
@Override
public ImageState createFromParcel(Parcel source) {
return new ImageState(source);
}
@Override
public ImageState[] newArray(int size) {
return new ImageState[size];
}
};
Uri savedUri;
public ImageState(Parcel source) {
super(source);
savedUri = source.readParcelable(Uri.class.getClassLoader());
}
public ImageState(final Parcelable superState) {
super(superState);
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeParcelable(savedUri, flags);
}
}
}