Thursday, July 4, 2013

Converting classes with the Parcelable protocol

Many good tutorials can be found about the Parcelable protocol in Android. If the Android framework has to serialize a set of values (either for AIDL-based IPC or for Intent-based message passing), it can serialize basic types or objects that implement the Parcelable protocol. In short, Parcelable objects are able to serialize their states into a Parcel object and they can retrieve their state from the serialized form.

But what happens if the two classes (the one which is serializing and the one which is retrieving its state from serialized form) are not the same? If you remember my post about classloading in Android, there is no way loading a class from another APK. If you pass a serialized object to another application whose codebase comes from a different APK, the target application has to rely on its own version of the class with the same name. Android framework does not verify that the sender and receiving classes are the same, they just have to have the same name (including package name).

Click here to download the example application.



There are two apps in the package. Actually, it is one of my first example programs, here is the description from a 2007 post of this blog. There are two applications, intentsender and intentreceiver. Intentsender has an internal intentreceiver plus it can talk to the external intentreceiver app. Both applications have a version of aexp.data.TwoNumbers class. In this example they are just slightly different - they log different messages.

What happens when you click on the "exp / int" (explicit Intent with internal intentreceiver) button. Both of these activities are residing in the intentsender APK therefore the class which will be used to serialize and deserialize the object are the same.

D/TwoNumbers1( 1090): serializing in TwoNumbers#1: i1: 2; i2: 3
D/TwoNumbers1( 1090): deserializing in TwoNumbers#1: i1: 2; i2: 3

But if you click on the "exp / ext" (explicit Intent with external intentreceiver) button, the message has to pass the APK boundary (it is processed in the intentreceiver app). Intentreceiver has nothing else but its own version of aexp.data.TwoNumbers. The log demonstrates it:

D/TwoNumbers1( 1207): serializing in TwoNumbers#1: i1: 4; i2: 5
D/TwoNumbers2( 1166): deserializing in TwoNumbers#2: i1: 4; i2: 5

Android framework did not care that the two TwoNumbers classes were not the same, it just called them blindly to serialize and deserialize. Actually, if you look into the Parcel implementation, you will see that the serialized version of the Parcelable only stores the fully qualified class name so it is not surprising that intentreceiver loaded whatever version of the class it could find under the given name. Fortunately, the two versions were compatible (they produced and consumed the same serialized stream) so there were no problems.

Why is this useful other than misleading yourself with classes that look the same but they aren't? If you want to expose just part of your serialized object to the receiver application, this trick comes handy. Otherwise it is best to avoid it.