We expose an AIDL service to third party developers. We'd like to return parcelable objects from this service, but we have concerns about backwards compatibility. By this, I mean clients compiled against version N of the parcelable, and a service compiled against version N+1 must work together.
From my tests, for simple flat objects (simple type fields only), backwards compatibility is possible, as long as new fields are parceled at the end of stream ... e.g.,
in.writeInt(field1);
in.writeInt(field2); // new field
however, when it comes to complex objects, things blow up. for example,
class D implements Parcelable {
int field1;
}
class C implements Parcelable {
List<D> ds;
}
if a second field is added to class D
,
class D implements Parcelable {
int field1;
int field2; // new field
}
unmarshalling of class C
s fail (i tried using Parcel.writeList()
and Parcel.writeParcelableArray()
).
This seems almost inconceivable that this case can't be handled. Sure, we can leave the old binder interface as-is, and create a new binder interface that returns objects of the new class with the additional fields, but for a service that returns oft-changing classes, this will result in a convoluted mess. For example, interface 1.0 returns a Person
. now we add a field, and we have a new binder interface 2.0 that returns Person2
objects, and so on.
This leaves us to using some more forgiving format, like JSON, to pass IPC data.
Any suggestions?
Clients compiled against version N of the Parcelable
and the AIDL interface will need to be supported by the Service
until the heat death of the universe, not just until N+1, unless you want to break a bunch of clients or are in position to force those developers to update their apps.
Sure, we can leave the old binder interface as-is
Any change to the AIDL itself means that you need a new service endpoint for the new protocol version, let alone changes to the Parcelable
definitions.
for example, interface 1.0 returns a Person. now we add a field, and we have a new binder interface 2.0 that returns Person2 objects, and so on.
Either:
Get it right the first time, so that you do not have an "oft-changing" public API, or
Use a Bundle
for the "oft-changing" aspects, since Bundle
is stable (e.g., a Person
has a properties
Bundle
for stuff you wish to glom onto your public API in between every-few-years major API revisions), or
Use a Bundle
in the first place, so your API is more passing around property bags, or
Switch to Serializable
, despite it being perhaps a bit slower, as it has the notion of versioning, or
Dump the binding pattern entirely and use the command pattern, with extras serving as the property bag
Your alternative of JSON is roughly analogous to using a Bundle
in the first place, except that you don't have to fuss with your own marshaling/unmarshaling code with a Bundle
.
Parcelable
specifically avoids versioning for speed reasons. That's why Parceable
is not designed for durable storage, when classes might change between when the data was saved and when the data was read in.