Proper Parcelable testing

Check out this Parcelable I've just written:

public class Thing implements Parcelable {  
  public static final Parcelable.Creator<Thing> CREATOR =
    new Parcelable.Creator<Thing>() {
      public Thing createFromParcel(Parcel in) {
        throw new RuntimeException("Oops");
      }

      // ...skipping boilerplate...
    };

  // ...so much boilerplate...
}

The RuntimeException thrown in the middle of createFromParcel() is a terrifyingly accurate simulation of poor code from my past.

It seems like you'd have a real bad time using this class, but you'd be surprised how hard it is to get it to crash. Check out my crappy Parcelable in action in this simple Activity:

public class MainActivity {

  Thing thing = new Thing();

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (savedInstanceState != null) {
      thing = savedInstanceState.getParcelable("key");
    }
  }

  @Override
  protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putParcelable("key", thing);
  }
}

Fire up the Activity and rotate the screen. Even though onCreate() is called multiple times, it doesn't crash. How can that be? Shouldn't it crash the moment we call getParcelable()?

The reason it doesn't crash is because sometimes, for optimization purposes, the system will reuse the exact same Parcelable instance you passed into putParcelable(), rather than going through the effort of creating a new object and unparcelling data. It's a nice optimization, but it means you can think your Parcelable code is working when it's not.

Having run into this multiple times, here are my two best practices with regard to Parcelables:

  1. Don't write Parcelable code manually. Use a tool like auto-value-parcel to do it for you. You can't write better code than it will generate for you.

  2. If somehow the situation calls for artisanal, hand-crafted Parcelables, then test your Parcelables using the "don't keep activities" developer option enabled. That way, you can guarantee that all your Parcelable code is being exercised by switching back and forth between your app and another app.

comments powered by Disqus