Writing Parcelable classes for Android

I’ve just released version 0.3 of Xydroid, my small Xymon monitoring app. It should now be a bit more suited to Android 2.0, and have just a couple fewer bugs in it.

As it turns out, the main problem with running under Android 2.0 for my app was de-parceling of a Parcelable representation of the XML files used by Xydroid. Up until now, I had just written out all the data in the tree when parceling, and read until no data was left when de-parceling. As it turns out, if this parcelable class is put into a bundle with other data, such as strings or integers, the de-parceling code will just continue reading along the bundle. The error I tended to encounter would be “Unmarshalling unknown type code 6946932” (or some other number), presumably as I tried to read a String from where an integer existed or similar.

The solution seems to be to write how much data you are planning to parcel as the first bit of information, and only read back this many chunks before passing control back. This works both in 2.0 and 1.5/1.6, whereas reading until there was no more data seemed to work in 1.5/1.6 but not 2.0 — though this might just have been pure luck.

Update: I thought I had better actually show how I ended up implementing my Parcelable classes, so here is a quick example of how to wrap a HashMap in something parcelable:

ParcelTest.java

import java.util.HashMap;
 
import android.os.Parcel;
import android.os.Parcelable;
 
public class ParcelTest implements Parcelable {
    private HashMap map;
 
    public ParcelTest() {
        map = new HashMap();
    }
 
    public ParcelTest(Parcel in) {
        map = new HashMap();
        readFromParcel(in);
    }
 
    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
        public ParcelTest createFromParcel(Parcel in) {
            return new ParcelTest(in);
        }
 
        public ParcelTest[] newArray(int size) {
            return new ParcelTest[size];
        }
    };
 
    @Override
    public int describeContents() {
        return 0;
    }
 
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(map.size());
        for (String s: map.keySet()) {
            dest.writeString(s);
            dest.writeString(map.get(s));
        }
    }
 
    public void readFromParcel(Parcel in) {
        int count = in.readInt();
        for (int i = 0; i < count; i++) {
            map.put(in.readString(), in.readString());
        }
    }
 
    public String get(String key) {
        return map.get(key);
    }
 
    public void put(String key, String value) {
        map.put(key, value);
    }
}

Update 2: Stefan requested that I post some code showing how to use this implementation, so here is a supplier and a consumer activity, using this Parcelable object:

ExampleSupplierActivity.java

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
 
public class ExampleSupplierActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ParcelTest p = new ParcelTest();
        p.put("green", "go");
        p.put("yellow", "wait");
        p.put("red", "stop");
 
        Bundle b = new Bundle();
        b.putParcelable("com.example.trafficlight", p);
 
        Intent i = new Intent(this, ExampleConsumerActivity.class);
        i.putExtras(b);
 
        startActivity(i);
    }
}

ExampleConsumerActivity.java

import android.app.Activity;
import android.os.Bundle;
 
public class ExampleConsumerActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Bundle b = getIntent().getExtras();
 
        ParcelTest p = b.getParcelable("com.example.trafficlight");
 
        String red = p.get("red");
        // ...
    }
}

Disclaimer: This isn’t the actual code from my system, and I haven’t tested it. If you want efficient code, you will likely need to change things; If you want safe code, you will most certainly need to do things in a less careless way. If you find any bugs, please leave a comment.

2 thoughts on “Writing Parcelable classes for Android

  1. Stefan

    Thanks for sharing this implementation. That’s already very helpful. Maybe if you find the time, you can add some information (sample code) on how to actually use the ParcelTest class in the application.

  2. Sebastian

    Hey, nice, but I don’t get the in.readInt() or in.writeInt() used on the fors.

    Seems that readInt() returns you the size of the in parcel, but the reference says:

    public final void writeInt (int val)

    Write an integer value into the parcel at the current dataPosition(), growing dataCapacity() if needed.

    public final int readInt ()

    Read an integer value from the parcel at the current dataPosition().

    Can you explain that a little more? regards,

Leave a Reply

Your email address will not be published. Required fields are marked *