Understand parcellable

satya - Tuesday, August 23, 2011 10:13:37 AM

Goal

I need a quick mechanism to store a java tree of objects and retrieve them. I looked at preferences which can store key/value based pairs including a set of values for a given key.

I wondered if I can use parcellables to serialize java objects. Or may be there are libraries that can already do this.

Focus of this page is to expore parcellables in that light.

satya - Tuesday, August 23, 2011 10:15:30 AM

See how I have used preferences for saving any state including widget state before

See how I have used preferences for saving any state including widget state before

This abstraction is quite useful but a bit limited. May be one can extend this to include the set of values and not just single values.

satya - Tuesday, August 23, 2011 10:20:32 AM

api for Parcel: the parcellable container

api for Parcel: the parcellable container

satya - Tuesday, August 23, 2011 10:22:32 AM

Parcellable api

Parcellable api

satya - Tuesday, August 23, 2011 10:24:17 AM

Parcel API doc strongly advises not to use this API for persistence

Parcel is not a general-purpose serialization mechanism. This class (and the corresponding Parcelable API for placing arbitrary objects into a Parcel) is designed as a high-performance IPC transport. As such, it is not appropriate to place any Parcel data in to persistent storage: changes in the underlying implementation of any of the data in the Parcel can render older data unreadable.

satya - 4/4/2013 9:48:12 AM

See this article for understanding the storage options

See this article for understanding the storage options

satya - 4/4/2013 9:50:25 AM

See how pareclables are used to manage view states

See how pareclables are used to manage view states

satya - 4/4/2013 9:56:54 AM

parcelable describecontents

parcelable describecontents

Search for: parcelable describecontents

satya - 4/4/2013 10:05:12 AM

Here is Diane Hackborns comments on this

Here is Diane Hackborns comments on this

satya - 4/4/2013 10:09:31 AM

This method should return a 0 or 1

The value of 0 means there is no special significance to this object. if it is 1, android seem to assume that this object contains a file descriptor that cannot be passed across system process boundaries...

satya - 4/4/2013 3:40:48 PM

Can I write a null into a parcel?

Can I write a null into a parcel?

Search for: Can I write a null into a parcel?

satya - 4/4/2013 3:50:17 PM

There are mixed opinions on this and how to deal with it.

however the docs seem to say one should be able to write a null attribute and read it as null!!

satya - 4/4/2013 4:05:03 PM

writeToParcel flags

writeToParcel flags

Search for: writeToParcel flags

satya - 4/4/2013 4:06:51 PM

ARCELABLE_WRITE_RETURN_VALUE

ARCELABLE_WRITE_RETURN_VALUE

Search for: ARCELABLE_WRITE_RETURN_VALUE

satya - 4/5/2013 8:38:36 AM

Can only base classes implement parcelable CREATOR methods?

Can only base classes implement parcelable CREATOR methods?

Search for: Can only base classes implement parcelable CREATOR methods?

satya - 4/5/2013 8:47:39 AM

inheritance parcelable CREATOR

inheritance parcelable CREATOR

Search for: inheritance parcelable CREATOR

satya - 4/5/2013 8:52:22 AM

Here is some discussion on this

Here is some discussion on this

Importantly by contract it is not required that the inherited classes provide this method, (at least for now). So it is legal, probably, to return an appropriate class in the create method!

Havent' tried it.

satya - 4/7/2013 9:21:05 AM

Reading an object from Parcleable

Reading an object from Parcleable

Search for: Reading an object from Parcleable

satya - 4/7/2013 9:23:36 AM

readParcelable

readParcelable

Search for: readParcelable

satya - 4/7/2013 9:28:12 AM

you may need to use this at times


in.readParceleable(LocationType.class.getClassLoader());

satya - 4/7/2013 9:31:05 AM

See some of the discussion here

See some of the discussion here

satya - 4/7/2013 9:40:15 AM

Parcelable is such a nasty way to serialize objects

it may be very processor and memory efficient but incredibly error prone!!!

satya - 4/7/2013 9:45:08 AM

A simple example


public class User
implements Parcelable
{
    //Add more field types later
    public String userid;
    public String username;
    
    public User(String inuserid, String inusername)
    {
        userid = inuserid;
        username = inusername;
    }
    public static final Parcelable.Creator<User> CREATOR
        = new Parcelable.Creator<User>() {
            public User createFromParcel(Parcel in) {
                return new User(in);
            }
            
            public User[] newArray(int size) {
                return new User[size];
            }
    }; //end of creator

    //
    @Override
    public int describeContents() {
        return 0;
    }
    
    public User(Parcel in)
    {
        userid = in.readString();
        username = in.readString();
    }
    @Override
    public void writeToParcel(Parcel dest, int flags) 
    {
        dest.writeString(userid);
        dest.writeString(username);
    }
    public String toString()
    {
        return userid + "/" + username;
    }
}//eof-class

satya - 4/7/2013 9:45:44 AM

Involving other parcelable components


public class ParseObjectEssentials
implements Parcelable
{
   //Add more field types later
   public Date createdAt;
   public User createdBy;
   public Date lastUpdatedAt;
   public User lastUpdatedBy;
   
   public ParseObjectEssentials(Date createdAt, User createdBy,
         Date lastUpdatedAt, User lastUpdatedBy) {
      super();
      this.createdAt = createdAt;
      this.createdBy = createdBy;
      this.lastUpdatedAt = lastUpdatedAt;
      this.lastUpdatedBy = lastUpdatedBy;
   }
    public static final Parcelable.Creator<ParseObjectEssentials> CREATOR
       = new Parcelable.Creator<ParseObjectEssentials>() {
         public ParseObjectEssentials createFromParcel(Parcel in) {
             return new ParseObjectEssentials(in);
         }
         
         public ParseObjectEssentials[] newArray(int size) {
             return new ParseObjectEssentials[size];
         }
    }; //end of creator

   //
   @Override
   public int describeContents() {
      return 0;
   }
   
   public ParseObjectEssentials(Parcel in)
   {
      createdAt = new Date(in.readLong());
      createdBy = (User)in.readParcelable(User.class.getClassLoader());
      lastUpdatedAt = new Date(in.readLong());
      lastUpdatedBy = (User)in.readParcelable(User.class.getClassLoader());
   }
   @Override
   public void writeToParcel(Parcel dest, int flags) 
   {
      dest.writeLong(this.createdAt.getTime());
      dest.writeParcelable(createdBy, flags);
      dest.writeLong(lastUpdatedAt.getTime());
      dest.writeParcelable(lastUpdatedBy, flags);
      
   }
}//eof-class

satya - 4/7/2013 10:42:43 AM

You may want to document the order of writes and reads


public void writeToParcel(Parcel parcel, int flags) 
   {
      //Order
      //tablename, fieldlist, field values, essentials
      
      //write the tablename
      parcel.writeString(this.getTablename());

satya - 4/19/2013 4:21:18 PM

ParcelableFileDescriptor

ParcelableFileDescriptor

Search for: ParcelableFileDescriptor

satya - 4/19/2013 4:28:30 PM

Bundle.hasFileDescriptors()

Bundle.hasFileDescriptors()

Search for: Bundle.hasFileDescriptors()

satya - 4/19/2013 4:35:58 PM

ParcelFileDescriptor.java

ParcelFileDescriptor.java

Search for: ParcelFileDescriptor.java

satya - 4/19/2013 4:37:51 PM

Here is how the parcelable interface is satisfied


/* Parcelable interface */
    public int describeContents() {
        return Parcelable.CONTENTS_FILE_DESCRIPTOR;
    }

    /**
     * {@inheritDoc}
     * If {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set in flags,
     * the file descriptor will be closed after a copy is written to the Parcel.
     */
    public void writeToParcel(Parcel out, int flags) {
        out.writeFileDescriptor(mFileDescriptor);
        if ((flags&PARCELABLE_WRITE_RETURN_VALUE) != 0 && !mClosed) {
            try {
                close();
            } catch (IOException e) {
                // Empty
            }
        }
    }

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

satya - 4/20/2013 7:48:31 AM

Bundle.java

Bundle.java

Search for: Bundle.java

satya - 4/20/2013 8:00:54 AM

A good resource for understanding parcelables is here: docs for Parcel

A good resource for understanding parcelables is here: docs for Parcel

satya - 4/20/2013 8:01:50 AM

Here is the nature of a parcel from that docs

Container for a message (data and object references) that can be sent through an IBinder. A Parcel can contain both flattened data that will be unflattened on the other side of the IPC (using the various methods here for writing specific types, or the general Parcelable interface), and references to live IBinder objects that will result in the other side receiving a proxy IBinder connected with the original IBinder in the Parcel.

satya - 4/20/2013 8:09:20 AM

What can you write into a parcel


Primitives
arrays (4byte length + data)
Parcelables that write classnames to restore
Parcelables that relyon CREATOR methods
bundles (key value pairs)
Proxy objects using IBinder
File descriptor objects that are proxies

satya - 4/20/2013 8:15:58 AM

ParcelFileDescriptor.java

ParcelFileDescriptor.java

Search for: ParcelFileDescriptor.java

satya - 4/20/2013 8:18:30 AM

source code link for ParcelFileDescriptor.java

source code link for ParcelFileDescriptor.java

satya - 4/20/2013 8:20:33 AM

Here is the api doc for ParcelFileDescriptor

Here is the api doc for ParcelFileDescriptor

satya - 4/20/2013 8:22:48 AM

How does a ParcelFileDescriptor comes into existence?


Open a file directlry as parcelfiledescriptor
Use a raw fd as an input
Use another ParcelFileDescriptor as an input (when recreating a parcelable)

satya - 4/20/2013 8:23:18 AM

Here is an interesting note on this class: writeToParcel

Flatten this object in to a Parcel. If PARCELABLE_WRITE_RETURN_VALUE is set in flags, the file descriptor will be closed after a copy is written to the Parcel.

satya - 4/20/2013 8:23:48 AM

PARCELABLE_WRITE_RETURN_VALUE

PARCELABLE_WRITE_RETURN_VALUE

Search for: PARCELABLE_WRITE_RETURN_VALUE

satya - 4/20/2013 8:54:56 AM

what we know about this so far

if the object being written has any open resources then you can close them

somehow it is linked to the return value of a function. this is not very clear

example of resources: open files, connections, etc perhaps

satya - 4/20/2013 9:06:32 AM

here is the api doc for this flag

here is the api doc for this flag

satya - 4/20/2013 9:07:48 AM

what the api seem to insist


the object being written is a return value!!

satya - 4/20/2013 9:15:10 AM

Here is what the API says

Flag for use with writeToParcel(Parcel, int): the object being written is a return value, that is the result of a function such as "Parcelable someFunction()", "void someFunction(out Parcelable)", or "void someFunction(inout Parcelable)". Some implementations may want to release resources at this point.

satya - 4/20/2013 9:16:25 AM

Does it mean...

the object being written is already a parceled version of the original?? Because the someFunction() must have returned a Parceled object!!

satya - 4/20/2013 12:07:40 PM

Or is there more to this idea of what is returned vs something that is created on the stack?

Or is there more to this idea of what is returned vs something that is created on the stack?

satya - 4/20/2013 12:10:23 PM

Or is it indicating to write just values and not the underlying state?

Or is it indicating to write just values and not the underlying state?

satya - 4/20/2013 12:11:43 PM

I have posted this question to android groups. here is the link, should one answer

I have posted this question to android groups. here is the link, should one answer

satya - 4/20/2013 12:34:49 PM

Or is the object being written is something that needs to be returned from a function?


void someothrfunction(Parcel reply)
{
   Parcelable p = somefunction();
   reply.writeParcelable(p,PARCELABLE_WRITE_RETURN_VALUE);
}

Parcelable somefunction()
{
}

If so why am I not doing this in most if not all places?

satya - 4/20/2013 12:39:46 PM

This flag is clearly and instruction to the implementer of the parcelable


SO implements Parcelable
{
  writeToParcel(flags)
  {
     //decide what to do differently
}};

So as a user of this object do I need to know the implication of this flag on a case by case basis? Or is there a general rule that applies to all parcelables for a consistent behavior?

In android SDK this seem to indicate release resources!!! How do I know if a Parcelable has resources to be released or not? So Do I need to know this on a case by case basis. Such as the case when I use ParcelFileDescriptor whose documentation says that using this flag will do xyz.

satya - 4/20/2013 12:42:43 PM

or is the real distinction between value and reference?

or is the real distinction between value and reference?

satya - 4/20/2013 4:14:48 PM

The flag is used only when writing to the parcel. It is not applicable when reading

if you notice only when you are writing this flag is passed. This flag is not available while reading. Should this give an clue to its intention..

satya - 4/20/2013 4:16:09 PM

Also intent.putExtra(Parcelable)

Does not have a method to send the parcelable flag!! So it is probably applicable only in the larger local context of a Parcelable that is explicitly writing to parcelables!

satya - 5/11/2013 8:26:33 AM

hasfiledescriptors()

hasfiledescriptors()

Search for: hasfiledescriptors()

satya - 5/11/2013 8:50:13 AM

Another tack at this file descriptors

Apparently for Android SDK it is important to know if a Parceled object contains file descriptors. This information is used to implement the method Bundle.hasFileDescriptors(). This method in turn is used to prevent objects with descriptors being given to the system process. For example code like this can be seen in the core Android SDK source


//Taken from ActivityManagerService.java
if (intent.hasFileDescriptors()) {
         throw new IllegalArgumentException("File descriptors passed in Intent");
}