Parcelableを使ってみる

今回はParcelableを使用してみようと思います

Parcelableとは

あるclassのInstanceをParcelに保存したり、または、復元したりするために実装するInterface(原文ここ)。Orientation変更したりするとSystemによりActivityがonDestroy()まで呼ばれ、再度、Activityの生成Processが実行されるので、Orientation変更前の状態を保持したい場合にそのinstanceのclassにParcelable interfaceを実装する感じですね

実装の要点をまとめます

1. Parcelable interfaceを実装する

public class ParcelableData implements Parcelable{
	
	@Override
	public int describeContents() {
		return 0;
	}

	@Override
	public void writeToParcel(Parcel dest, int flags) {

	}
    }
}

2. Parcelable#writeToParcel()をoverrideし、引数に渡されるParcel objectに保存したいfieldを書き込む

@Override
public void writeToParcel(Parcel dest, int flags) {
	dest.writeString(mString);
	dest.writeInt(mInt);
	dest.writeBooleanArray(mBooleanArray);
	dest.writeTypedList(mAnotherParcelableDataList);
}

3. Parcelable.Createrを実装する

public static final Parcelable.Creator<ParcelableData> CREATOR
	
	= new Parcelable.Creator<ParcelableData>() {
		@Override
		public ParcelableData createFromParcel(Parcel in) {
			return new ParcelableData(in);
		}
		@Override
		public ParcelableData[] newArray(int size) {
			return new ParcelableData[size];
		}
	};
}

4. Parcelable.Creater#createFromParcel()で返すobejctを生成するためのprivate constructor内で引数で渡されるParcel objectからfieldの値を復元

private ParcelableData(Parcel in) {
       mString = in.readString();
       mInt = in.readInt();
       in.readBooleanArray(mBooleanArray);
       in.readTypedList(mAnotherParcelableDataList, AnotherParcelableData.CREATOR);
}

5. Activity#onSaveInstanceState()内で引数に渡されるBundle objectにParcelable objectを保存する

 @Override
protected void onSaveInstanceState(Bundle outState) {
	super.onSaveInstanceState(outState);
	outState.putParcelable(KEY_PARCELABLE, mData);
}

6. Activity#onCreate()内で引数に渡されるBundle objectからParcelable objectを復元する

@Override
public void onCreate(Bundle savedInstanceState) {
       if(savedInstanceState != null){
       	   mData = savedInstanceState.getParcelable(KEY_PARCELABLE);
       }else{
       	   mData = createParcelableData();
       }
}

気付いた点をまとめます

  • BooleanはArrayでしか保存出来ない

それでは実装です

Activity

package com.android.practice.parcelable;

import java.util.ArrayList;
import java.util.List;

import com.android.practice.parcelable.ParcelableData.AnotherParcelableData;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MyActivity extends Activity {
    
	private static final String TAG = "MyActivity";
	
	private static final String KEY_PARCELABLE = "key_parcelable";
	
	private static final String STRING = "String";
	private static final int INT = 1;
	private static final boolean BOOLEAN = false;
	private static final String ANOTHER_STRING = "Another String";
	
	private static final String CHANGED_STRING = "Changed String";
	private static final int CHANGED_INT = 2;
	private static final boolean CHANGED_BOOLEAN = true;
	private static final String CHANGED_ANOTHER_STRING = "Changed Another String";
	
	private ParcelableData mData;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        //Activity#onCreate()内で引数に渡されるBundle objectからParcelable objectを復元する
        if(savedInstanceState != null){
        	mData = savedInstanceState.getParcelable(KEY_PARCELABLE);
        }else{
        	mData = createParcelableData();
        }
        
        Button changeButton = (Button)findViewById(R.id.change_button);
        changeButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				mData.setString(CHANGED_STRING);
				mData.setInt(CHANGED_INT);
				mData.setBoolean(CHANGED_BOOLEAN);
				mData.getAnotherParcelableDataList().get(0).setString(CHANGED_ANOTHER_STRING);
				setUpTextViews();
			}
		});
        
        setUpTextViews();
    }
    
    @Override
	protected void onStart() {
    	Log.d(TAG, "onStart");
		super.onStart();
	}
    
    @Override
	protected void onResume() {
    	Log.d(TAG, "onResume");
		super.onResume();
	}
    
    //Activity#onSaveInstanceState()内で引数に渡されるBundle objectにParcelable objectを保存する
    @Override
	protected void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		outState.putParcelable(KEY_PARCELABLE, mData);
	}

	@Override
	protected void onPause() {
    	Log.d(TAG, "onPause");
		super.onPause();
	}
    
    @Override
	protected void onStop() {
    	Log.d(TAG, "onStop");    	
		super.onStop();
	}
    
    @Override
	protected void onDestroy() {
    	Log.d(TAG, "onDestroy");
		super.onDestroy();
	}
    
    private ParcelableData createParcelableData(){
    	List<AnotherParcelableData> anotherDataList = new ArrayList<AnotherParcelableData>();
    	anotherDataList.add(new AnotherParcelableData(ANOTHER_STRING));
    	return new ParcelableData(STRING, INT, BOOLEAN, anotherDataList);
    }

	private void setUpTextViews(){
    	((TextView)findViewById(R.id.string_text)).setText(mData.getString());
    	((TextView)findViewById(R.id.int_text)).setText(String.valueOf(mData.getInt()));
    	((TextView)findViewById(R.id.boolean_text)).setText(String.valueOf(mData.getBoolean()));
    	((TextView)findViewById(R.id.another_string_text)).setText(String.valueOf(mData.getAnotherParcelableDataList().get(0).getString()));
    }
}

Parcelableを実装したclass

package com.android.practice.parcelable;

import java.util.List;

import android.os.Parcel;
import android.os.Parcelable;

//Parcelable interfaceを実装する
public class ParcelableData implements Parcelable{
	
	private static final String TAG = "ParcelableData";
	
	private String mString;
	private int mInt;
	private boolean[] mBooleanArray = new boolean[1];
	private List<AnotherParcelableData> mAnotherParcelableDataList;
	
	public ParcelableData(){
	}
	
	public ParcelableData(String string, int i, boolean bool, List<AnotherParcelableData> anotherParcelableDataList){
		mString = string;
		mInt = i;
		mBooleanArray[0] = bool;
		mAnotherParcelableDataList = anotherParcelableDataList;
	}
	
	public String getString(){
		return mString;
	}
	
	public void setString(String string){
		mString = string;
	}
	
	public int getInt(){
		return mInt;
	}
	
	public void setInt(int i){
		mInt = i;
	}
	
	public boolean getBoolean(){
		return mBooleanArray[0];
	}
	
	public void setBoolean(boolean bool){
		mBooleanArray[0] = bool;
	}
	
	public List<AnotherParcelableData> getAnotherParcelableDataList(){
		return mAnotherParcelableDataList;
	}
	
	public void setAnotherParcelableDataList(List<AnotherParcelableData> anotherParelableDataList){
		mAnotherParcelableDataList = anotherParelableDataList;
	}

	@Override
	public int describeContents() {
		return 0;
	}

	//Parcelable#writeToParcel()をoverrideし、引数に渡されるParcel objectに保存したいfieldを書き込む
	@Override
	public void writeToParcel(Parcel dest, int flags) {
		dest.writeString(mString);
		dest.writeInt(mInt);
		//BooleanはArrayでしか保存出来ない
		dest.writeBooleanArray(mBooleanArray);
		dest.writeTypedList(mAnotherParcelableDataList);
	}
	
	//Parcelable.Createrを実装する
	public static final Parcelable.Creator<ParcelableData> CREATOR
	
	= new Parcelable.Creator<ParcelableData>() {
		@Override
		public ParcelableData createFromParcel(Parcel in) {
			return new ParcelableData(in);
		}
		@Override
		public ParcelableData[] newArray(int size) {
			return new ParcelableData[size];
		}
	};
	//Parcelable.Creater#createFromParcel()で返すobejctを生成するためのprivate constructor内で
	//引数で渡されるParcel objectからfieldの値を復元
	private ParcelableData(Parcel in) {
        mString = in.readString();
        mInt = in.readInt();
        in.readBooleanArray(mBooleanArray);
        in.readTypedList(mAnotherParcelableDataList, AnotherParcelableData.CREATOR);
    }
	
	public static class AnotherParcelableData implements Parcelable{
		
		private String mString;
		
		public AnotherParcelableData(){
			
		}
		
		public AnotherParcelableData(String string){
			mString = string;
		}
		
		public String getString(){
			return mString;
		}
		
		public void setString(String string){
			mString = string;
		}
		
		@Override
		public int describeContents() {
			return 0;
		}

		@Override
		public void writeToParcel(Parcel dest, int flags) {
			dest.writeString(mString);
			
		}
		
		public static final Parcelable.Creator<AnotherParcelableData> CREATOR
		= new Parcelable.Creator<AnotherParcelableData>() {
			public AnotherParcelableData createFromParcel(Parcel in) {
				return new AnotherParcelableData(in);
			}

			public AnotherParcelableData[] newArray(int size) {
				return new AnotherParcelableData[size];
			}
		};
		
		private AnotherParcelableData(Parcel in) {
			mString = in.readString();
	    }
		
	}
}

と、こんな感じです。

ちなみに、Manifest fileに下記の様にandroid:configChange="orientation"を指定すればorientation変更の際にActivityの再起動を避ける事が出来ます

<activity android:name="MyActivity" android:configChanges="keyboardHidden|orientation">

HashTag #Java, #Android, #Parcelable, #Parcel, #Bandle