Android Parcelable Objects

By Paulus, 14 November, 2013

If you need to pass data from one Intent to another, you need to use Bundles. Passing basic data types such as int, boolean, byte, byte arrays, etc are easily accomplished by calling their respective method; putBoolean() for example. You can even pass ArrayLists, but only if the ArrayList contains basic data types. In the event that you want to pass an ArrayList containing objects of a custom class, you must implement Parcelable on that class. Looking at the PhoneNumber class:

public class PhoneNumber {

	/**
	 * SQLite index key.
	 */
	private int id;

	/**
	 * Name of the person for the number.
	 */
	private String contactName;

	/**
	 * The phone number.
	 */
	private String contactNumber;

	/**
	 * The list the phone number is in.
	 */
	private int list;

	/**
	 * Creates a new PhoneNumber object.
	 */
	public PhoneNumber() {
		// TODO Auto-generated constructor stub
	}

	/**
	 * Creates a new Phonenumber object assigning it a name and number.
	 * @param name The name of the person the number belongs to.
	 * @param number The number.
	 */
	public PhoneNumber(String name, String number) {
		this.setName(name);
		this.setNumber(number);
	}

	/**
	 * Creates a new PhoneNumber object assigning it a database id, a name, number, and assigned list.
	 * @param id The database id.
	 * @param name The name of the person the number belongs to.
	 * @param number The number.
	 * @param list The list in which this number is found in.
	 */
	public PhoneNumber(int id, String name, String number, int list) {
		this.id = id;
		this.contactName = name;
		this.contactNumber = number;
		this.list = list;
	}

	/**
	 * @return the id
	 */
	public int getId() {
		return id;
	}

	/**
	 * @param id the id to set
	 */
	public void setId(int id) {
		this.id = id;
	}

	/**
	 * @return the contact's name
	 */
	public String getName() {
		return contactName;
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.contactName = name;
	}

	/**
	 * @return the phone number
	 */
	public String getNumber() {
		return contactNumber;
	}

	/**
	 * @param number the number to set
	 */
	public void setNumber(String number) {
		this.contactNumber = number;
	}

	/**
	 * @return the list
	 */
	public int getList() {
		return list;
	}

	/**
	 * @param list the list to set
	 */
	public void setList(int list) {
		this.list = list;
	}
}

We must do the following to make it Parcelable:

  1. Implement Parcelable, as mentioned earlier.
  2. Create an additional constructor that takes single argument of Parcel
  3. Override the describeContents and writeToParcel methods
  4. Create a static field called CREATOR

Number one is straight forward, and is nothing new unless you started programming Java 5 minutes ago. If that is the case, I wouldn't recommend diving into Android development until you have a firm grasp on Java.

public class PhoneNumber implements Parcelable

You need the additional constructor for when the bundle is opened, so the receiving activity knows how to re-create the object. It's important to note the way you packed the data because you will unpack it in the same order.

public PhoneNumber(Parcel in) {
	String[] sdata = new String[2];
	int[] idata = new int[2];

	in.readStringArray(sdata);
	in.readIntArray(idata);

	this.contactName = sdata[0];
	this.contactNumber = sdata[1];
	this.id = idata[0];
	this.list = idata[1];
}

There are two methods that need to be overriden, but you only have to write code for one of them. When I looked at the documentation for Parcelable, I felt the documentation was lacking. When looking for a better explaination I found that others feel that its functionality is unfinished. When overriding this method you can simply leave it as:

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

Overriding the writeToParcel depends on how your class is setup. This is where the heavy lifting is done when the ArrayList is bundled.

@Override
public void writeToParcel(Parcel dest, int flags) {
	dest.writeStringArray(new String[] {
			this.contactName,
			this.contactNumber
	});

	dest.writeIntArray(new int[] {
			this.id,
			this.list
	});
}

The final piece is creating the static field called CREATOR. Like the describeContents method, you don't do much aside from changing the return statements to create an object of the class you want to be Parcelable.

public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {

	@Override
	public Object createFromParcel(Parcel source) {
		return new PhoneNumber(source);
	}

	@Override
	public Object[] newArray(int size) {
		return new PhoneNumber[size];
	}

};

Once your custom class is "Parcelable" you can bundle it like so:

ArrayList<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>();
Bundle bundle = new Bundle();
phoneNumbers.add(new PhoneNumber());
bundle.putParcelableArrayList(phoneNumbers);