Parcelable Interface Overview
In one of my earlier posts, I mentioned writing an article about FOSOAuthBundle integration with an Android client. To keep that article to the point, I need to explain some concepts beforehand. One of the important concepts is the Android Parcelable interface that allows data to be transferred between different processes/threads. Certain network operations with Android such as authentication with OAuth2 and then fetching data from a REST endpoint should be performed in the background in order not to block the UI thread. This requires data to be fetched by a service (I have opted for Intent Services in my implementation) in the background and then passed back to the calling activity/fragment with a result callback. This is where the Parcelable interface comes into play.
Basically, the Parcelable interface allows your classes to be flattened inside a message container called a Parcel to facilitate high performance inter process communication. The received parcel data can then be unflattened to generate object/entity instances.
A Basic Parcelable Example
A Parcelable implementation is pretty straight forward. Override the necessary methods called writeToParcel() and describeContents(), add a static field called CREATOR which generates instances of your Parcelable class from a Parcel, and overload the class constructor which expects a Parcel as a parameter and calls the readFromParcel() utility method. Here is a basic example:
public class Conversation implements Parcelable { // ... protected String lastComment; protected Integer messageCount; protected Date createdAt; // ... // ... public Conversation(Parcel in) { readFromParcel(in); } @Override public void writeToParcel(Parcel out, int flags) { // ... out.writeString(lastComment); out.writeInt(messageCount); out.writeSerializable(createdAt); // ... } private void readFromParcel(Parcel in) { // ... lastComment = in.readString(); messageCount = in.readInt(); createdAt = (Date) in.readSerializable(); // ... } public static final Parcelable.CreatorCREATOR = new Parcelable.Creator () { public Conversation createFromParcel(Parcel in) { return new Conversation(in); } public Conversation[] newArray(int size) { return new Conversation[size]; } }; @Override public int describeContents() { return 0; } }
Nested Parcelable Classes
Lets say we have a Conversation class instance with an embedded User class instance that holds some information about a user that has initiated the conversation. Basically, we are talking about an one-to-one embedded mapping. In this case, if the fromUser property holds a User instance, then writing the user data to parcel would be accomplished as shown below:
@Override public void writeToParcel(Parcel out, int flags) { // ... out.writeParcelable(fromUser, flags); // ... }
Reading the user data from parcel:
private void readFromParcel(Parcel in) { // ... fromUser = in.readParcelable(User.class.getClassLoader()); // ... }
If you have a one-to-many embedded relationship such as a list of messages in a conversation, then the syntax would change as follows:
@Override public void writeToParcel(Parcel out, int flags) { // ... out.writeList(messages); // ... }
Reading from the parcel:
private void readFromParcel(Parcel in) { // ... in.readList(messages, Message.class.getClassLoader()); // ... }
Boolean Types
Android API does not have a method to write a single boolean value to a parcel. In this case, you can utilize the writeInt() method as shown below:
@Override public void writeToParcel(Parcel out, int flags) { // ... out.writeInt(booleanValue ? 1 : 0); // ... }
Reading from the parcel:
private void readFromParcel(Parcel in) { // ... booleanValue = in.readInt() == 1; // ... }
Enum Types
To flatten an Enum type in a parcel, simply implement the Parcelable interface for the Enum type. Here is an example:
public enum Status implements Parcelable { STARTED, PAUSED, FINISHED; public static final Parcelable.CreatorCREATOR = new Parcelable.Creator () { public Status createFromParcel(Parcel in) { return Status.values()[in.readInt()]; } public Status[] newArray(int size) { return new Status[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(ordinal()); } }
When writing to the parcel, treat it as a nested Parcelable class:
@Override public void writeToParcel(Parcel out, int flags) { // ... out.writeParcelable(status, flags); // ... }
Reading from the parcel:
private void readFromParcel(Parcel in) { // ... status = in.readParcelable(Status.class.getClassLoader()); // ... }
I took this one step further.
ReplyDeleteInstead of using a public constructor I just used a private method.
Any thoughts on this?
My code looks like this:
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public myClass createFromParcel(Parcel in) {
return new myClass().readFromParcel(in);
}
public myClass[] newArray(int size) {
return new myClass[size];
}
};
Hi nice article,
ReplyDeleteCan you please provide an emaple on how to use the parceble inside the same Activity?
Thanks in advance!
Thanks, this helped :) A lot of boilerplate... But apparently it's worth it: http://www.developerphil.com/parcelable-vs-serializable/
ReplyDelete