Skip to main content

A Parcelable Tutorial for Android

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.Creator CREATOR = 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.Creator CREATOR = 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());
    // ...
}

Comments

  1. I took this one step further.
    Instead 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];
    }
    };

    ReplyDelete
  2. Hi nice article,

    Can you please provide an emaple on how to use the parceble inside the same Activity?

    Thanks in advance!

    ReplyDelete
  3. Thanks, this helped :) A lot of boilerplate... But apparently it's worth it: http://www.developerphil.com/parcelable-vs-serializable/

    ReplyDelete

Post a Comment

Popular posts from this blog

Securing Symfony2 REST services with FOSOAuthServerBundle

Overview In my previous article, I wrote about setting up a Symfony2 REST service using FOSRestBundle. However, this REST service was behind a firewall protected by a generic form_login provider. Not really ideal if you wish to open your REST API to other applications. So in this article, I will try to explain how to set up FOSOAuthServerBundle to protect your REST API methods using OAuth2. Before we start getting into the gritty details, it is a good idea to have a look at the official OAuth2 documentation . Let's begin... FOSOAuthServerBundle Installation You have to install v1.1.0 of FOSOAuthServerBundle if you are using Symfony 2.0.x. If not, see the docs . First, add the following entries to your deps file: [FOSOAuthServerBundle] git=git://github.com/FriendsOfSymfony/FOSOAuthServerBundle.git target=bundles/FOS/OAuthServerBundle version=origin/1.1.x [oauth2-php] git=git://github.com/FriendsOfSymfony/oauth2-php.git Run the vendors script to install these...

Adding post-login logic to FOSUserBundle

Having finally figured out how to use FOSUserBundle in my project, I decided to keep track of all logins next. The implementation turned out to be a breeze thanks to Symfony2's security listener mechanism. As usual, the first step is to create a MongoDB document for this purpose. This is a very simple document that contains a user's id, session id, IP address, and login date. src/Acme/UserBundle/Document/LoginHistory.php namespace Acme\UserBundle\Document; class LoginHistory { protected $id; protected $userId; protected $sessionId; protected $ip; protected $createdAt; /** * Get id * * @return custom_id $id */ public function getId() { return $this->id; } /** * Set userId * * @param int $userId */ public function setUserId($userId) { $this->userId = $userId; } /** * Get userId * * @return int $userId */ public function getUserId...

Symfony 2 + DoctrineMongoDBBundle + FOSUserBundle Tutorial

It's been a while I have not added an entry to my blog. I have been busy playing with Symfony 2, MongoDB, and FOSUserBundle for a while now so here is a tutorial to integrate Symfony 2, MongoDB, and FOSUserBundle. Objectives * Install DoctrineMongoDBBundle * Install FOSUserBundle * Create user model ** Utilize groups ** Add additional properties to user model * Create customized registration form and handler At this point I am going to assume that you have a running Symfony 2 and MongoDB installation in place already and a basic understanding of how to configure Symfony 2 services. I will also exclude view related steps from this tutorial. Setting Up DoctrineMongoDBBundle Add the following to your deps file: [doctrine-mongodb] git=http://github.com/doctrine/mongodb.git [doctrine-mongodb-odm] git=http://github.com/doctrine/mongodb-odm.git [DoctrineMongoDBBundle] git=http://github.com/symfony/DoctrineMongoDBBundle.git target=/bundles/Symfony/Bundle/Doctrin...