:package: Android Parcelables made easy through code generation.
= Parceler
image:https://badges.gitter.im/johncarl81/parceler.svg[link="https://gitter.im/johncarl81/parceler?utmsource=badge&utmmedium=badge&utmcampaign=pr-badge&utmcontent=badge"] image:https://travis-ci.org/johncarl81/parceler.png?branch=master["Build Status", link="https://travis-ci.org/johncarl81/parceler"] image:https://maven-badges.herokuapp.com/maven-central/org.parceler/parceler-api/badge.svg["Maven Central", link="https://maven-badges.herokuapp.com/maven-central/org.parceler/parceler-api"]
Have a question? http://stackoverflow.com/questions/ask?tags=parceler[Ask it on StackOverflow.]
Found an issue? https://github.com/johncarl81/parceler/issues/new[Please report it.]
In Android, http://developer.android.com/reference/android/os/Parcelable.html[Parcelables] are a great way to serialize Java Objects between Contexts. http://www.developerphil.com/parcelable-vs-serializable/[Compared] with traditional Serialization, Parcelables take on the order of 10x less time to both serialize and deserialize. There is a major flaw with Parcelables, however. Parcelables contain a ton of boilerplate code. To implement a Parcelable, you must mirror the
writeToParcel()and
createFromParcel()methods such that they read and write to the Parcel in the same order. Also, a Parcelable must define a
public static final Parcelable.Creator CREATORin order for the Android infrastructure to be able to leverage the serialization code.
Parceler is a code generation library that generates the Android Parcelable boilerplate source code. No longer do you have to implement the Parcelable interface, the
writeToParcel()or
createFromParcel()or the
public static final CREATOR. You simply annotate a POJO with
@Parceland Parceler does the rest. Because Parceler uses the Java JSR-269 Annotation Processor, there is no need to run a tool manually to generate the Parcelable code. Just annotate your Java Bean, compile and you are finished. By default, Parceler will serialize the fields of your instance directly:
@Parcel public class Example { String name; int age;
public Example() {}public Example(int age, String name) { this.age = age; this.name = name; }
public String getName() { return name; }
public int getAge() { return age; }
Be careful not to use private fields when using the default field serialization strategy as it will incur a performance penalty due to reflection.
To use the generated code, you may reference the generated class directly, or via the
Parcelsutility class:
To dereference the
@Parcel, just call the
Parcels.unwrap()method:
Example example = Parcels.unwrap(wrapped); example.getName(); // Andy
Of course, the wrapped
Parcelablecan be added to an Android Bundle to transfer from Activity to Activity:
Bundle bundle = new Bundle();
And dereferenced in the
onCreate()method:
This wrapping and unwrapping technique plays well with the Intent Factory pattern. In addition, Parceler is supported by the following libraries:
@Parcelannotated beans to be used with the
@Extrainjection.
ParcelerArgsBundleradapter to wrap and unwrap
@Parcelannotated beans with fragment parameters.
@Parcelannotated beans and automatically unwraps them when using
@InjectExtra.
@Parcelannotated beans and https://github.com/excilys/androidannotations/wiki/ParcelerIntegration[automatically wraps/unwraps] them when using
@Extra,
@FragmentArg,
@InstanceStateand other
Bundlerelated annotations.
=== Parcel attribute types Only a select number of types may be used as attributes of a
@Parcelclass. The following list includes the mapped types:
byte
double
float
int
long
char
boolean
String
IBinder
Bundle
SparseArrayof any of the mapped types*
SparseBooleanArray
ObservableField
List,
ArrayListand
LinkedListof any of the mapped types*
Map,
HashMap,
LinkedHashMap,
SortedMap, and
TreeMapof any of the mapped types*
Set,
HashSet,
SortedSet,
TreeSet,
LinkedHashSetof any of the mapped types*
Parcelable
Serializable
@Parcel
*Parcel will error if the generic parameter is not mapped.
Parceler also supports any of the above types directly. This is especially useful when dealing with collections of classes annotated with
@Parcel:
Parcelable listParcelable = Parcels.wrap(new ArrayList());
==== Polymorphism Note that Parceler does not unwrap inheritance hierarchies, so any polymorphic fields will be unwrapped as instances of the base class. This is because Parceler opts for performance rather than checking
.getClass()for every piece of data.
@Parcel public class Example { public Parent p; @ParcelConstructor Example(Parent p) { this.p = p; } }
@Parcel public class Parent {}
Example example = new Example(new Child()); System.out.println("%b", example.p instanceof Child); // true example = Parcels.unwrap(Parcels.wrap(example));
Refer to the <> section for an example of working with polymorphic fields.
=== Serialization techniques
Parceler offers several choices for how to serialize and deserialize an object in addition to the field-based serialization seen above.
==== Getter/setter serialization Parceler may be configured to serialize using getter and setter methods and a non-empty constructor. In addition, fields, methods and constructor parameters may be associated using the
@ParcelPropertyannotation. This supports a number of bean strategies including immutability and traditional getter/setter beans.
To configure default method serialization, simply configure the
@Parcelannotation with
Serialization.BEAN:
@Parcel(Serialization.BEAN) public class Example { private String name; private int age; private boolean enabled;
public String getName() { return name; } public void setName(String name) { this.name = name; }public int getAge() { return age; } public void setAge(int age) { this.age = age; }
public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; }
To use a constructor with serialization, annotate the desired constructor with the
@ParcelConstructorannotation:
@Parcel(Serialization.BEAN) public class Example { private final String name; private final int age; private boolean enabled;
@ParcelConstructor public Example(int age, String name, boolean enabled) { this.age = age; this.name = name; this.enabled = enabled; }public String getName() { return name; }
public int getAge() { return age; }
public boolean isEnabled() { return enabled; }
If an empty constructor is present, Parceler will use that constructor unless another constructor is annotated.
==== Mixing getters/setters and fields You may also mix and match serialization techniques using the
@ParcelPropertyannotation. In the following example,
firstNameand
lastNameare written to the bean using the constructor while
firstNameis read from the bean using the field and
lastNameis read using the
getLastName()method. The parameters
firstNameand
lastNameare coordinated by the parameter names
"first"and
"last"respectfully.
@Parcel public class Example { @ParcelProperty("first") String firstName; String lastName;
@ParcelConstructor public Example(@ParcelProperty("first") String firstName, @ParcelProperty("last") String lastName){ this.firstName = firstName; this.lastName = lastName; }public String getFirstName() { return firstName; }
@ParcelProperty("last") public String getLastName() { return lastName; }
For attributes that should not be serialized with Parceler, the attribute field, getter or setter may be annotated by
@Transient.
Parceler supports many different styles centering around the POJO. This allows
@Parcelannotated classes to be used with other POJO based libraries, including the following:
==== Static Factory support As an alternative to using a constructor directly, Parceler supports using an annotated Static Factory to build an instance of the given class. This style supports Google's https://github.com/google/auto/tree/master/value[AutoValue] annotation processor / code generation library for generating immutable beans. Parceler interfaces with AutoValue via the
@ParcelFactoryannotation, which maps a static factory method into the annotated
@Parcelserialization:
@AutoValue @Parcel public abstract class AutoValueParcel {
@ParcelProperty("value") public abstract String value();@ParcelFactory public static AutoValueParcel create(String value) { return new AutoValue_AutoValueParcel(value); }
AutoValue generates a different class than the annotated
@Parcel, therefore, you need to specify which class Parceler should build in the
Parcelsutility class:
And to deserialize:
==== Custom serialization
@Parcelincludes an optional parameter to include a manual serializer
ParcelConverterfor the case where special serialization is necessary. This provides a still cleaner option for using Parcelable classes than implementing them by hand.
The following code demonstrates using a
ParcelConverterto unwrap the inheritance hierarchy during deserialization.
@Parcel public class Item { @ParcelPropertyConverter(ItemListParcelConverter.class) public List itemList; } @Parcel public class SubItem1 extends Item {} @Parcel public class SubItem2 extends Item {}
public class ItemListParcelConverter implements ParcelConverter> { @Override public void toParcel(List input, Parcel parcel) { if (input == null) { parcel.writeInt(-1); } else { parcel.writeInt(input.size()); for (Item item : input) { parcel.writeParcelable(Parcels.wrap(item), 0); } } }
@Override public List fromParcel(Parcel parcel) { int size = parcel.readInt(); if (size < 0) return null; List items = new ArrayList(); for (int i = 0; i < size; ++i) { items.add((Item) Parcels.unwrap(parcel.readParcelable(Item.class.getClassLoader()))); } return items; }
Parceler is also packaged with a series of base classes to make Collection conversion easier located under the
org.parceler.converterpackage of the api. These base classes take care of a variety of difficult or verbose jobs dealing with Collections including null checks and collectin iteration. For instance, the above
ParcelConvertercould be written using the `ArrayListParcelConverter':
public class ItemListParcelConverter extends ArrayListParcelConverter { @Override public void itemToParcel(Item item, Parcel parcel) { parcel.writeParcelable(Parcels.wrap(item), 0); }
@Override public Item itemFromParcel(Parcel parcel) { return Parcels.unwrap(parcel.readParcelable(Item.class.getClassLoader())); }
=== Classes without Java source For classes whose corresponding Java source is not available, one may include the class as a Parcel by using the
@ParcelClassannotation. This annotation may be declared anywhere in the compiled source that is convenient. For instance, one could include the
@ParcelClassalong with the Android Application:
@ParcelClass(LibraryParcel.class) public class AndroidApplication extends Application{ //...
Multiple
@ParcelClassannotations may be declared using the
@ParcelClassesannotation.
In addition, classes referenced by
@ParcelClassmay be configured using the
@Parcelannotation. This allows the serialization configuration through any parameter available on the
@Parcelannotation including the serialization technique or classes to analyze.
One useful technique is the ability to define global custom converters for a type:
@ParcelClass( value = LibraryParcel.class, annotation = @Parcel(converter = LibraryParcelConverter.class))
This allows for fine grained control over a class that isn't available for direct modification.
=== Advanced configuration
==== Skipping analysis It is a common practice for some libraries to require a bean to extend a base class. Although it is not the most optimal case, Parceler supports this practice by allowing the configuration of what classes in the inheritance hierarchy to analyze via the analyze parameter:
@Parcel(analyze = {One.class, Three.class}) class One extends Two {} class Two extends Three {}
In this example, only fields of the
Oneand
Threeclasses will be serialized, avoiding both the
BaseClassand
Twoclass parameters.
==== Specific wrapping
The Parcels utility class looks up the given class for wrapping by class. For performance reasons this ignores inheritance, both super and base classes. There are two solutions to this problem. First, one may specify additional types to associate to the given type via the
implementationsparameter:
class ExampleProxy extends Example {} @Parcel(implementations = {ExampleProxy.class}) class Example {}
ExampleProxy proxy = new ExampleProxy();
Second, one may also specify the class type when using the
Parcels.wrap()method:
ExampleProxy proxy = new ExampleProxy();
==== Configuring Proguard
To configure Proguard, add the following lines to your proguard configuration file. These will keep files related to the
Parcelsutilty class and the
Parcelable
CREATORinstance:
-keep interface org.parceler.Parcel -keep @org.parceler.Parcel class * { *; }
== Getting Parceler
You may download Parceler as a Maven dependency:
org.parceler parceler 1.1.12 provided org.parceler parceler-api 1.1.12
or Gradle:
implementation 'org.parceler:parceler-api:1.1.12'
Or from http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.parceler%22[Maven Central].
Copyright 2011-2015 John Ericksen
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and