Parcelable, simplified

Since Kotlin 1.1.4, the Kotlin Android Extensions was updated with a new experimental feature, replacing the boilerplate-ridden way of implementing a Parcelable interface with an annotation. Since then, the 1.1.6 release added IDE support – but what is a Parcelable, and why should you use it?

What’s a Parcelable?

Parcelable
is an Android-specific interface, created to be far more efficient than Serializable, and also to fix problems around regular Java serialization. It is not used exclusively on Android, though. A couple of platform-related features (like Binders and AIDL) work only with Parcelable objects, others support both serializable and parcelable (for example adding extras to a Bundle or Intent) objects.

Not to confuse with Serializable, which is a standard Java interface. Used for Intents, and passing data between components of the app, or between multiple apps. You simply mark a class Serializable by implementing the interface, and Java will automatically serialize it in certain situations. But simplicity has its cost: this approach uses reflection, creates a lot of temporary objects, and causes GC.

This is where Parcelable takes the crown – instead of using reflection, one has to implement serialization by hand, making it more efficient, and run significantly faster
. A huge drawback is of course that that this means quite a few extra lines of boiler plate. Also, it is hard to maintain: modifying a class requires updating the implemented functions as well. The following example illustrates how to make a simple model class parcelable.

public class Book implements Parcelable {

    public String title;
    public String author;
    public int year;

    public Book(final String title, final String author, final int year) {
        this.title = title;
        this.author = author;
        this.year = year;
    }

    private Book(Parcel in) {
        this.title = in.readString();
        this.author = in.readString();
        this.year = in.readInt();
    }

    public static final Creator CREATOR = new Creator() {
        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeString(title);
        parcel.writeString(author);
        parcel.writeInt(year);
    }
}

Quite the boilerplate! Also, writing this by hand is very error-prone: adding a new class variable requires modifying multiple functions manually.

The Kotlin way

The Kotlin Android Extensions is usually used for
eliminating findViewById()
calls

, the 1.1.4 release extended it with an experimental feature for simplifying Parcelable implementation into using an annotation. Hence the experimental label, it is necessary to add the following statement to the build script:

androidExtensions {
    experimental = true
}

So the class above translates to this:

@Parcelize
class Book(val title: String, val author: String, val year: Int) : Parcelable

Sidenote: the compiler will warn that This class implements Parcelable but does not provide a CREATOR field
– the @SuppressLint("ParcelCreator")
annotation fixes this.

Taking a look at decompiled bytecode (with @Metadata
annotations omitted, for clarity) shows that a standard parcelable class is generated.

public final class Book implements Parcelable {
   @NotNull
   private final String title;
   @NotNull
   private final String author;
   private final int year;
   public static final Book.Creator CREATOR = new Book.Creator();

   @NotNull
   public final String getTitle() {
      return this.title;
   }

   @NotNull
   public final String getAuthor() {
      return this.author;
   }

   public final int getYear() {
      return this.year;
   }

   public Book(@NotNull String title, @NotNull String author, int year) {
      Intrinsics.checkParameterIsNotNull(title, "title");
      Intrinsics.checkParameterIsNotNull(author, "author");
      super();
      this.title = title;
      this.author = author;
      this.year = year;
   }

   public final int describeContents() {
      return 0;
   }

   @AvoidUninitializedObjectCopyingCheck
   public final void writeToParcel(@NotNull Parcel parcel, int flags) {
      Intrinsics.checkParameterIsNotNull(parcel, "parcel");
      parcel.writeString(this.title);
      parcel.writeString(this.author);
      parcel.writeInt(this.year);
   }

   public static class Creator implements android.os.Parcelable.Creator {
      @NotNull
      public final Book[] newArray(int size) {
         return new Book[size];
      }

      @NotNull
      @AvoidUninitializedObjectCopyingCheck
      public final Object createFromParcel(@NotNull Parcel in) {
         Intrinsics.checkParameterIsNotNull(in, "in");
         return new Book(in.readString(), in.readString(), in.readInt());
      }
   }
}

The bottom line is Parcelable is super fast, and every Android developer should use it instead of Serializable objects, where applicable. Even though traditionally it does require some boilerplate code and extra development time, Kotlin Android Extensions reduces this to the use of a single annotation. In a way, it is similar to data classes
, as in instead of writing boilerplate code, the same functionality is achieved with a single keyword.

Even though labelled as experimental
, it works good, and is reliable, so I’m not advising against using it in production. Also, JetBrains is working on it (the 1.1.6
release added bug fixes and IDE support), so hopefully it will be part of the stable release soon.

稿源:Kotlin Development (源链) | 关于 | 阅读提示

本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » 移动开发 » Parcelable, simplified

喜欢 (0)or分享给?

专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录