Introduction
To pass custom data structures across process boundaries using Binder, Android requires those structures to implement the Parcelable interface. This is drastically more efficient than Java Serialization because it relies on highly optimized custom marshalling directly into a memory buffer.
Parcelable Interface Implementation
Implementing a Parcelable requires fulfilling two main contracts: writing the object state into a Parcel and providing a static CREATOR to reconstruct the object from a Parcel.
package com.example.android.ipc;
import android.os.Parcel;
import android.os.Parcelable;
public class UserProfile implements Parcelable {
public int id;
public String name;
public UserProfile(int id, String name) {
this.id = id;
this.name = name;
}
protected UserProfile(Parcel in) {
id = in.readInt();
name = in.readString();
}
@Override
public int describeContents() {
return 0; // Usually 0, unless dealing with FileDescriptors
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
}
public static final Creator<UserProfile> CREATOR = new Creator<UserProfile>() {
@Override
public UserProfile createFromParcel(Parcel in) {
return new UserProfile(in);
}
@Override
public UserProfile[] newArray(int size) {
return new UserProfile[size];
}
};
}
Parcel writeToParcel and createFromParcel
The order in which data is written in writeToParcel() must strictly match the order it is read in the UserProfile(Parcel in) constructor. The Parcel object acts as a contiguous array of bytes. When writing, data is appended; when reading, a pointer advances through the buffer. A mismatch in reading/writing logic will result in corrupted data or a RuntimeException.
To use this in AIDL, you must declare the class in an AIDL file so the compiler knows it exists.
// UserProfile.aidl
package com.example.android.ipc;
parcelable UserProfile;
Passing Complex Objects over Binder
Once declared, the Parcelable can be used in other AIDL interfaces. Notice the use of directional tags (in, out, inout), which dictate who does the marshalling.
// IUserManager.aidl
package com.example.android.ipc;
import com.example.android.ipc.UserProfile;
interface IUserManager {
// Client serializes, Service deserializes
void updateUser(in UserProfile profile);
// Service returns an initialized object
UserProfile getUser(int id);
}
When passing an out parameter, the client passes an empty object, and the service populates it. The framework optimizes this to prevent unnecessary memory allocations during IPC.