Difficulty: 3 of 5
Description:
I was looking for a way to implement a lightweight web service communication.
First I saw this post by Lex ( :src: Calling a web service from Android) about kSOAP. It drew my attention. But I did some reading and decided that this implementation is too heavy for the Android platform. I wanted my code to go easy on the resources. So I did some further research and decided to go with the kXML-RPC which is a lightweight implementation of the XML-RPC protocol.
It wasn't all strait forward though since this library is actually meant to work with the J2ME SDK and it is not 100% compatible with the Android SDK.
Using some docs a debugger and the tutorial plusminus posted here (:src: Doing HTTP Post with Android) I managed to make this library work on Android.
Here is a small guide on how to make it all work together.
1. Download the kXML-RPC sources (http://sourceforge.net/projects/kxmlrpc).
2. Download the kXML2 sources
(http://sourceforge.net/project/showfile ... e_id=58653). You need it since the kXML-RPC lib depends on it to do the XML parsing.
3. Add those packages to your project.
4. Now to make it work with android you need to modify the "execute" method at the org.kxmlrpc.XmlRpcClient class.
Using java Syntax Highlighting
- /**
- * An Android specific implementation of the "execute" method
- */
- public Object execute(String method, Vector params, Context context) throws Exception {
- KXmlSerializer xw = null;
- XmlRpcWriter writer = null;
- XmlRpcParser parser = null;
- RequestQueue con = null;
- ByteArrayOutputStream bos = null;
- ByteArrayInputStream bis = null;
- try {
- // Prepare the arguments for posting
- bos = new ByteArrayOutputStream();
- xw = new KXmlSerializer();
- xw.setOutput(new OutputStreamWriter(bos));
- writer = new XmlRpcWriter(xw);
- writer.writeCall(method, params);
- xw.flush();
- if (debug)
- Log.d(DEBUG_TAG, bos.toString());
- byte[] request = bos.toByteArray();
- bis = new ByteArrayInputStream(request);
- Map<String, String> headers = new HashMap<String, String>();
- headers.put("Content-Type", "text/xml");
- XmlRpcEventHandler eventHandler = new XmlRpcEventHandler();
- // Create the connection and post the arguments
- con = new RequestQueue(context);
- con.queueRequest(url, "POST", headers, eventHandler, bis, request.length, false);
- con.waitUntilComplete();
- ByteArrayInputStream in = new ByteArrayInputStream(eventHandler.getBytes());
- // Parse response from server
- KXmlParser xp = new KXmlParser();
- xp.setInput(new InputStreamReader(in));
- parser = new XmlRpcParser(xp);
- result = parser.parseResponse();
- } catch (Exception x) {
- Log.e(DEBUG_TAG + ".error", x.getMessage());
- }//end try/catch/finally
- if (result instanceof Exception)
- throw (Exception) result;
- return result;
- }//end execute( String, Vector )
Parsed in 0.037 seconds, using GeSHi 1.0.8.4
5. And here is the event handler class that will get for the server response. (This is based on the code posted by plusminus).
Using java Syntax Highlighting
- /**
- * This class receives the server response
- */
- private class XmlRpcEventHandler implements EventHandler {
- private final String DEBUG_TAG = "XmlRpcEventHandler";
- private ByteArrayBuffer m_byteArray = new ByteArrayBuffer(20);
- XmlRpcEventHandler() {
- }
- public void data(byte[] bytes, int len) {
- m_byteArray.append(bytes, 0, len);
- }
- public void endData() {
- Log.d(DEBUG_TAG + ".endData", new String(m_byteArray.toByteArray()));
- }
- public void status(int arg0, int arg1, int arg2, String s) {
- Log.d(DEBUG_TAG + ".status", "status [" + s + "]");
- }
- public void error(int i, String s) {
- Log.d(DEBUG_TAG + ".error", "error [" + s + "]");
- }
- public void handleSslErrorRequest(int arg0, String arg1, SslCertificate arg2) {
- }
- public void headers(Iterator arg0) {
- }
- public void headers(Headers arg0) {
- }
- public byte[] getBytes() {
- return m_byteArray.toByteArray();
- }
- }
Parsed in 0.034 seconds, using GeSHi 1.0.8.4
6. Now you are almost done. Except there is a problem with the kXML-RPC library. It throws an exception while parsing the return values.
I believe it is a bug in the library. And here is the fix.
Open the "parseValue" method at the org.kxmlrpc.XmlRpcParser class and replace it with this one.
Using java Syntax Highlighting
- Object parseValue() throws XmlPullParserException, IOException {
- Object result = null;
- parser.require(XmlPullParser.START_TAG, null, "value");
- parser.nextTag();
- parser.require( XmlPullParser.START_TAG, null, null );
- String name = parser.getName();
- if( name.equals("string") )
- result = parser.nextText();
- else if( name.equals("i4") || name.equals("int") )
- result = new Integer
- ( Integer.parseInt( parser.nextText().trim() ) );
- else if( name.equals("boolean") )
- result = new Boolean( parser.nextText().trim().equals("1") );
- else if( name.equals("dateTime.iso8601") )
- result = IsoDate.stringToDate
- ( parser.nextText() );
- else if( name.equals("base64") )
- result = Base64.decode( parser.nextText() );
- else if( name.equals("struct") )
- result = parseStruct();
- else if( name.equals("array") )
- result = parseArray();
- // kxmlrpc does not currently support the XML-RPC double data type
- // the temporary workaround is to process double values as strings
- else if( name.equals("double") )
- result = parser.nextText();
- // return as text if the tag is not part of XML-RPC grammar
- // this is a temporary workaround as we need to be able to return
- // XML and HTML as a whole long String without terminating with </value>
- else result = parser.nextText();
- parser.require( XmlPullParser.END_TAG, null, name );
- parser.nextTag();
- parser.require( XmlPullParser.END_TAG, "", "value" );
- return result;
- }//end parseValue()
Parsed in 0.040 seconds, using GeSHi 1.0.8.4
7. Now you have an XML-RPC client on your Android platform.
If you want to test this code you can call it using a method similar to this one.
Using java Syntax Highlighting
- public void ping(Context context) {
- XmlRpcClient client = new XmlRpcClient("192.168.1.104/rpctest/server.php");
- Object res = null;
- try {
- res = client.execute("getResponse", new Vector(), context);
- } catch (Exception e) {
- new ExceptionNotification(m_context, e.getMessage());
- }
- Log.d(DEBUG_TAG + ".ping", res.toString());
- }
Parsed in 0.036 seconds, using GeSHi 1.0.8.4
8. And that's a wrap.
Last final note. To test it all in action I used Keith Devens PHP library for the server. You can grab it here: (http://keithdevens.com/software/xmlrpc)
The logcat output of my test was:
Using java Syntax Highlighting
- D/XmlRpcEventHandler.status(11544): status [OK]
- D/XmlRpcEventHandler.endData(11544): <?xml version="1.0" ?>
- D/XmlRpcEventHandler.endData(11544): <methodResponse>
- D/XmlRpcEventHandler.endData(11544): <params>
- D/XmlRpcEventHandler.endData(11544): <param>
- D/XmlRpcEventHandler.endData(11544): <value>
- D/XmlRpcEventHandler.endData(11544): <string>Hello World</string>
- D/XmlRpcEventHandler.endData(11544): </value>
- D/XmlRpcEventHandler.endData(11544): </param>
- D/XmlRpcEventHandler.endData(11544): </params>
- D/XmlRpcEventHandler.endData(11544): </methodResponse>
- D/WebServiceClient.ping(14583): Hello World
Parsed in 0.037 seconds, using GeSHi 1.0.8.4
Hope it helped you to gain some understanding on how to implement XML-RPC with web services.
Regards,
Daniel





and
The app does not run since beta 0.9. Has anyone found a solution. The class it is not happy about is the RequestQueue.queueRequest method.
