AutoValue is a tool to eliminate cumbersome codes for value classes. For value classes, we commonly need to implement equals, hashCode, and toString. Those are really repetitive and easy to make hard-to-spot bugs if you miss to implement every method properly when you add a new field.
AutoValue provides an easier way to create immutable value classes using an @AutoValue annotation. To use AutoValue, there are two simple steps. First, add @AutoValue annotation into your value class. Then, make your class and fields as abstract.
AutoValue generates a subclass while compiling. If you have “Cannot resolve symbol …“ warning, build your project. Then, it will be resolved properly.
After building your project, you can see the generated subclass, AutoValue_Album. It implements equals, hashCode, and toString. Also, it checks the nullability according to @NonNull and @Nullable annotation.
Parcelable was introduced in Android to store values mush fast than Serializable. It is important to make it parcelable as mush as possible because mobile devices can gain performance improvement a lot from that. To make value classes as parcelable, there are some requirements.
Classes must implement Parcelable interface.
Classes implementing the Parcelable interface must also have a non-null static field called CREATOR of a type that implements the Parcelable.Creator interface.
Even though there is only one field variable, you need to write many lines to implement Parcelable like this to fit requirements.
Whenever you want to make parcelable value classes, you need to implement those. Like equals and other methods, those are also boilerplate codes. By default, AutoValue doesn’t support Parcelable because it was implemented mainly for Java not for Android. However, it supports to make extensions. One extention, called auto-value-parcel, support it so we can remove those code. To use that extension, you first need to add below dependency into your build.gradle.
Then, just add implments Parcelable in your class. That’ it! You don’t have to implement CREATOR, writeToParcel and etc. Parcel extension generates those for you.
Here is the generated class from parcel extension.
Parcel extension support all of the types supported by Parcel class. However, there is a bit performance problem since it uses generic methods for List or custom classes. Let’s say you now have List data in your class.
Parcel extension generates writeToParcel method like this.
The performance problem happens from the writeList method. Value classes implementing Parcelable interface have a non-null static field CREATOR. That field has a logic to construct an object again from Parcel. Generic methods, such as writeList and writeParcelable, write a full value class path to access CREATOR field later. For example, it uses 64 bytes for the Album class if the full class path is sangsoonam.github.io.model.Album. Each character takes 2 bytes and it has 32 characters.
There is another method called writeTypedList. This method writes doesn’t wrtie the full class path. To read the data written with writeTypedList, you need to use a readTypedList method. readTypedList method has a Creator parameter so the value class knows how to construct an object without knowing a full class path. In short, wrtieList writes some info to access CREATOR later but writeTypedList doesn’t since it is set in the code.
For custom classes, check a writeTypedObject method.
You may think that is a small difference, but it is actually quite big. For the same data, it could be 344 bytes vs 44 bytes. If you want to see, check this repository and run unit tests.
To use non generic method, you need to define a custom de/serialization logic. TypeAdapter allows you to do but it is optional because it requries a small runtime component. To use that, you need to add this dependency.
Then, create a ListTypeAdapter implementing TypeAdapter in the Artist class.
Finally, add @ParcelAdapter annotation with ListTypeAdapter class to define a custom de/serialization.