Saturday, July 24, 2010

Extending Android XML-RPC to support regular Java serialization

For my current Android project I wanted to have some communication with a server component. After spending some time on finding a decent solution I arrived at XML-RPC.

A client would use this code to ask the server the sum of two numbers:

int sum = (Integer) client.call("Calculator.add", 2, 4);

The server side would have a class “Calculator” with a method “add”, like so:

public int add(int i1, int i2) {
    return i1 + i2;
}

Nice and clean!

For the client (Android) I use Android XML-RPC and for the server side I use use Apache WS XML-RPC. It is not possible to use Apache WS XML-RPC on Android out of the box, as there are some issues relating to the core Java classes that are supported on Android. I am sure you could get it running with some effort, but then you still will add hundreds of kilobytes to your app because of the jar files that need to be included. Android XML RPC is around 50 kilobytes, which is much nicer from an end-user perspective.

By default there is a limited set of “data types” that can be used and I want to use my own “data types” (like “User”, “Project”, etc.). Apache XML RPC has a special mode “enabledForExtensions”, if you check that option it will give you access to the use of longs, shorts, bytes, floats, DOM nodes, instances of java.io.Serializable, or JAXB objects. This is only handy in a use case that has Apache XML RPC on both client and server. So not in my case!

Luckily, it is easy to extend Android XML RPC to allow other data types. You just have to add some code to the XMLRPCSerializer class. As I only wanted support for deserializing objects I added an extra if clause to the deserialize method:

} else if (typeNodeName.equals("ex:serializable")) {
    obj = null;
    String value = parser.nextText();
    BufferedReader reader = new BufferedReader(new StringReader(value));
    String line;
    StringBuffer sb = new StringBuffer();
    while ((line = reader.readLine()) != null) {
        sb.append(line);
    }

    ByteArrayInputStream bais = new ByteArrayInputStream(Base64Coder.decode(sb.toString()));
    ObjectInputStream ois = new ObjectInputStream(bais);
    obj = ois.readObject();
    parser.nextTag();
    // parser.require(XmlPullParser.END_TAG, null, ..

    return obj;

} else {

No comments:

Post a Comment