Understand parcellable

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.

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.

api for Parcel: the parcellable container

Parcellable api

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.

See this article for understanding the storage options

See how pareclables are used to manage view states

parcelable describecontents

Search for: parcelable describecontents

Here is Diane Hackborns comments on this

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...

Can I write a null into a parcel?

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

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

writeToParcel flags

Search for: writeToParcel flags

ARCELABLE_WRITE_RETURN_VALUE

Search for: ARCELABLE_WRITE_RETURN_VALUE

Can only base classes implement parcelable CREATOR methods?

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

inheritance parcelable CREATOR

Search for: inheritance parcelable CREATOR

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.

Reading an object from Parcleable

Search for: Reading an object from Parcleable

readParcelable

Search for: readParcelable


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

See some of the discussion here

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


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

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

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

ParcelableFileDescriptor

Search for: ParcelableFileDescriptor

Bundle.hasFileDescriptors()

Search for: Bundle.hasFileDescriptors()

ParcelFileDescriptor.java

Search for: ParcelFileDescriptor.java


/* 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];
        }
    };

Bundle.java

Search for: Bundle.java

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

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.


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

ParcelFileDescriptor.java

Search for: ParcelFileDescriptor.java

source code link for ParcelFileDescriptor.java

Here is the api doc for ParcelFileDescriptor


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

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.

PARCELABLE_WRITE_RETURN_VALUE

Search for: PARCELABLE_WRITE_RETURN_VALUE

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

here is the api doc for this flag


the object being written is a return value!!

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.

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

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

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

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


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?


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.

or is the real distinction between value and reference?

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..

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!

hasfiledescriptors()

Search for: hasfiledescriptors()

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");
}