Skip to content

Java Usage

Bradley Campbell edited this page Nov 27, 2016 · 11 revisions

Important: all information in this on this page is only relevant to PaperParcel 1.x. For information on the latest version of PaperParcel, visit http://grandstaish.github.io/paperparcel/

Basic usage:

PaperParcel works by building a Parcelable wrapper for each class annotated with @PaperParcel, e.g.

@PaperParcel
public final class Example {
  private final int test;

  public Example(int test) {
    this.test = test;
  }

  public int getTest() {
    return test;
  }
}

This example generates a class named ExampleParcel (the name is always in the format of {ClassName}Parcel) which is Parcelable and has the code to read and write an Example object to and from a Parcel. ExampleParcel has a public constructor that takes in an Example instance and has a public member variable named data in which you can retrieve your data class from later. The usage for this is simple, just "wrap" your instance in the generated wrapper class and pass it through an Intent or Bundle like so:

Example example = new Example(42);
savedInstanceState.putParcelable("example", new ExampleParcel(example));

Then extract it later:

Example example = null
if (savedInstanceState != null) {
  ExampleParcel exampleWrapper = savedInstanceState.getParcelable<ExampleParcel>("example");
  example = exampleWrapper.data;
}

PaperParcel class formatting rules

// TODO: update

PaperParcel makes the following assumptions about an annotated classes:

  • The primary constructor must have public or default visibility
  • The annotated class' member variable names must equal the primary constructor parameter names (ordering does not matter)
  • The number of member variables should equal the number of arguments in the primary constructor. Static and transient member variables are not included in this count.
  • For each member variable, either the member variable must be public (or default) or its accessor method must be named x(), isX(), getX(), where x is the member variable's name. Alternatively, the member variable can be annotated with @AccessorName to specify what the actual accessor name is. Additionally, the accessor method must have no parameters.

Using PaperParcels to wrap/unwrap your model classes

Rather than needing to use the generated wrappers directly, it may be preferable to use the convenience class PaperParcels to wrap/unwrap your objects instead.

Simply wrap like so:

Example example = new Example(42);
savedInstanceState.putParcelable("example", PaperParcels.wrap(example));

And extract using:

Example example = null;
if (savedInstanceState != null) {
  TypedParcelable<Example> exampleWrapper = savedInstanceState.getParcelable("example");
  example = PaperParcels.unwrap(exampleWrapper);
}

Making model classes Parcelable

When working with wrapper APIs, like described above, it can make it tricky to work with code that you don't control because the receiving code won't know how to unwrap it. Hence making your model classes implement Parcelable themselves is the preferred technique. Luckily, PaperParcel makes this easy using the PaperParcelable class found in the paperparcel-java7 module (PaperParcelable is defined as an interface with default methods in paperparcel-java8 if your app targets Android N+).

PaperParcelable can be used to make your data classes Parcelable like so:

@PaperParcel
public final class Example extends PaperParcelable {
  public static final PaperParcelable.Creator<Example> CREATOR 
      = new PaperParcelable.Creator<>(Example.class);

  private final int test;

  public Example(int test) {
    this.test = test;
  }

  public int getTest() {
    return test;
  }
}

Note that the CREATOR field is boilerplate code and is a great candidate for a Live Template.

PaperParcelable uses the aforementioned PaperParcels class to do the wrapping and unwrapping internally, so make sure you don't forget the @PaperParcel annotation on your data class because it'll require that there is a generated wrapper at runtime.

If you can't extend PaperParcelable, it's not required. Just implement Parcelable instead, and copy implementation for writeToParcel and describeContents (they're both 1-liners).

Generating a wrapper for a type makes it known to PaperParcel

PaperParcel allows you to use other @PaperParcel classes as properties without them being Parcelable themselves, e.g.:

// This class is Parcelable as it is going to be passed directly to 
// an Intent or Bundle without using the wrapping APIs in the calling 
// and receiving code.
@PaperParcel
public final class Person extends PaperParcelable {
  public static final PaperParcelable.Creator<Person> CREATOR 
      = new PaperParcelable.Creator<>(Person.class);

  long id; 
  String name;
  ContactInfo contactInfo;

  public Person(long id, String name, ContactInfo contactInfo) {
    this.id = id;
    this.name = name;
    this.contactInfo = contactInfo;
  }
}

// This class isn't Parcelable, but PaperParcel will use the generated 
// wrapper for this class when parcelling Person
@PaperParcel
public final class class ContactInfo {
  String phoneNumber;
  String address;

  public ContactInfo(String phoneNumber, String address) {
    this.phoneNumber = phoneNumber;
    this.address = address;
  }
}

This is encouraged to help reduce boilerplate code.

Singletons

If your model class has a public static final instance of itself named INSTANCE, then PaperParcel will just use this object and won't bother parcelling/unparcelling anything, e.g.:

@PaperParcel
public final class Example {
  public static final Example INSTANCE = new Example();

  private final int test = 42;

  private Example() {
  }
}

This applies for TypeAdapters too