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
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
satya - 4/4/2013 9:50:25 AM
See how pareclables are used to manage view states
satya - 4/4/2013 9:56:54 AM
parcelable describecontents
parcelable describecontents
satya - 4/4/2013 10:05:12 AM
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?
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:06:51 PM
ARCELABLE_WRITE_RETURN_VALUE
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
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
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: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
satya - 4/19/2013 4:28:30 PM
Bundle.hasFileDescriptors()
Bundle.hasFileDescriptors()
satya - 4/19/2013 4:35:58 PM
ParcelFileDescriptor.java
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 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
satya - 4/20/2013 8:18:30 AM
source code link for ParcelFileDescriptor.java
satya - 4/20/2013 8:20:33 AM
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
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: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()
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");
}