Thursday, August 13, 2009

Android and Google App Engine

As the second part to my little series on developing an application across GAE(Google App Engine) and Android, I am going to continue with the example from my previous post on creating a REST service on GAE. In this post I will go through a very simple Android application that will use Restlet to call a Restful service deployed in GAE. I will assume the user has some basic knowledge of developing an android application, although this will be a very simple example so it should be easy to follow.

First download Restlet from here, if you do not already have it. You will need at least version 2.0 milestone 3. Once this is ready add the org.restlet.android.jar to the classpath of the project.

First using the eclipse plugin for Android development create a new project, and an Activity. I am just going to use the Hello World activity that is created by the plugin as a starting point. I modified the layout for the page to look like the following, I just gave the TextView an id so that I could refer to it in the Activity.


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:id="@+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>



Now on to the activity itself, in this example the application will just call the Restful service running on the GAE when starting up. If the current date is before the expiration date a message will display telling the user when the app will expire, and if the application is already expired, it will display a message telling the user so. Obviously this example is not practical, and in a real application more would be done to provide a better user experience, but this is just to show how one could call the service.

In the onCreate method a handle to the TextView is created, and the text is set to display the correct message based on the expire date. A method called getExpireDate is called to do the actual checking of the date, parse the xml returned from the server, and generate the message string. You can see the class in all it's glory below.


package test.app;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.restlet.representation.Representation;
import org.restlet.resource.ClientResource;
import org.restlet.resource.ResourceException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class Test extends Activity {
private SimpleDateFormat df = new SimpleDateFormat("MMM d, yyyy HH:mm:ss a");
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView v = (TextView) findViewById(R.id.title);
v.setText(getExpireDate());
}

private String getExpireDate(){

String expireDate = "";
boolean expired = false;
String url = "http://10.0.2.2:8080";
ClientResource resource = new ClientResource(url);
try {
Representation r = resource.get();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dbf.newDocumentBuilder();
Document dom = builder.parse(new ByteArrayInputStream(r.getText().getBytes("UTF-8")));
Element root = dom.getDocumentElement();
NodeList nodes = root.getElementsByTagName("date");
System.out.println("");
// DomRepresentation rep = new DomRepresentation(r);
// NodeList nodes = rep.getDocument().getDocumentElement().getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if(node.getNodeName().equals("date")){
expireDate = node.getFirstChild().getNodeValue();
Date date = df.parse(expireDate);
Date today = new Date();
if(date.before(today))
expired=true;

}
}
} catch (ResourceException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

//create date string
String returnMessage="";
if(expired){
returnMessage = "The app expired on "+expireDate;
}else{
returnMessage = "The app will expire on "+expireDate;
}
return returnMessage;

}
}


I am using the Restlet libraries to call the service on GAE using a ClientResource, with the URL the service is running at, and then calling get on that URL. I am then using some DOM libraries that are included with Android to parse the xml result. Restlet does include some libraries to do this, but I couldn't get them to work properly, I'm not sure that they are fully supported in this release of Restlet for Android. Once the expire date is pulled out of the result, the comparison is done, and the display string generated. This is fairly straightforward and simple, and Restlet makes doing this very easy. The url that is used in the example should be the url that your application is running in under GAE, in the example above I was running on localhost, and 10.0.2.2 is the machine address that the android emulator uses to access a url running on your local machine.

The only other thing that is left to do is to enable the internet permission on android, to let Android and the users know that the application will be accessing the internet. If you don't know how to do that refer to the Android documentation, but I have included my full manifest for the example below. That's it!


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="test.app"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".Test"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="3" />

<uses-permission android:name="android.permission.INTERNET"></uses-permission>
</manifest>

3 comments:

Unknown said...

Thanks for the article. I am trying using webservices in Android..and very new to it. I want to know that is it possible to add a message body in the REST webservice call?

Unknown said...

To work in 2.0!
1. create map lib in project
copy org.restlet.jar in it. Add in project classpath.
2. Repair "http://10.0.2.2:8080"; in "http://10.0.2.2:8888/XXXXXXXX"; //check XXXX in browser (Android Virtual Machine) with http://10.0.2.2:8888 ...

JB said...

I really like and appreciate your post. Seriously appreciate it! Really Cool.