Hi Folks! Today I ‘m presenting you how to create an Android client for our WCF cloud hosted viewer.
Let me be clear about the purpose of this post: I am NOT going to teach you how to create an Android app from scratch, neither show how to get your development environment ready or your SDK installed. If you have never programmed on that platform, I would suggest you take a day or two to go through one of the numerous tutorial you can easily find on the web in order to get you started and comeback once you know how to run a basic “Hello World” application.
The focus of this post is to illustrate how to perform web service calls, parse the retrieved data and exploit them in our client app.
I – Performing REST web request from Android
The nice thing with REST requests is that they don’t require any additional third party API, it’s all part of the native Android SDK. You need to use the HttpClient.execute(HttpGet request) method with correct REST address of your webservice and you should be good to go for retrieving the Json message it returns. The Android platform will however require you to perform the call asynchronously, which is anyway a good thing, so you need to perform this call from an AsyncTask.
In order to make it easy to use, I created the following WebServiceConnector class, that wraps the webservice call in that convenient utility. I added the WebServiceResponseListener interface to encapsulate the asynchronous response, the the clients can just listen to that event and get notified when the service call succeed or fails. Source code is below:
//////////////////////////////////////////////////////////////////////////////////////////
// This class wraps a REST web service call and provides notification
// on success or failure of the call
//
//////////////////////////////////////////////////////////////////////////////////////////
public class WebServiceConnector
{
private WebServiceTask _webSrvTask;
private ArrayList<WebServiceResponseListener> _listeners =
new ArrayList<WebServiceResponseListener>();
//////////////////////////////////////////////////////////////////////////////////////
// WebServiceResponseListener Interface: provides notification
// webservice success or failure
//
//////////////////////////////////////////////////////////////////////////////////////
public interface WebServiceResponseListener
{
public void OnWebServiceSuccess(String JsonMsg);
public void OnWebServiceFailed(Exception ex);
}
public void addEventListener(WebServiceResponseListener listener)
{
_listeners.add(listener);
}
public void removeEventListener(WebServiceResponseListener listener)
{
_listeners.remove(listener);
}
private void OnWebServiceSuccess(String JsonMsg)
{
for (WebServiceResponseListener listener : _listeners)
{
listener.OnWebServiceSuccess(JsonMsg);
}
}
private void OnWebServiceFailed(Exception ex)
{
for (WebServiceResponseListener listener : _listeners)
{
listener.OnWebServiceFailed(ex);
}
}
//////////////////////////////////////////////////////////////////////////////////////
// The WebServiceConnector Class
//
//////////////////////////////////////////////////////////////////////////////////////
public WebServiceConnector(
String url,
String methodName,
WebServiceResponseListener listener)
{
_webSrvTask = new WebServiceTask(
url,
methodName);
_listeners.add(listener);
}
public void NewRequest()
{
_webSrvTask = new WebServiceTask(
_webSrvTask._url,
_webSrvTask._method);
}
void AddRequestParam(String param)
{
_webSrvTask._param = param;
}
public void Invoke()
{
_webSrvTask.execute();
}
//////////////////////////////////////////////////////////////////////////////////////
// An internal class that performs the asynchronous call to the web service
//
//////////////////////////////////////////////////////////////////////////////////////
private class WebServiceTask extends AsyncTask<Void, Void, Void>
{
String _url;
String _method;
String _param;
public WebServiceTask(
String url,
String method)
{
_url = url;
_method = method;
_param = null;
}
//////////////////////////////////////////////////////////////////////////////////
// Converts InputStream from webservice response to JSon String
//
//////////////////////////////////////////////////////////////////////////////////
private String ConvertStreamToString(InputStream is)
{
BufferedReader reader =
new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
try
{
while ((line = reader.readLine()) != null)
{
sb.append(line + "\n");
}
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
try
{
is.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
return sb.toString();
}
//////////////////////////////////////////////////////////////////////////////////
// Perform webservice request
//
//////////////////////////////////////////////////////////////////////////////////
private String GetJsonMessage(String address)
{
HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet(address);
HttpResponse response;
try
{
response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null)
{
InputStream instream = entity.getContent();
String result = ConvertStreamToString(instream);
instream.close();
return result;
}
return null;
}
catch (Exception e)
{
return null;
}
}
//////////////////////////////////////////////////////////////////////////////////
// Performs asynchronous call
//
//////////////////////////////////////////////////////////////////////////////////
@Override
protected Void doInBackground(Void... params)
{
try
{
String request = _url + _method;
if(_param != null)
request += _param;
String jsonMsg = GetJsonMessage(request);
if(jsonMsg != null)
{
OnWebServiceSuccess(jsonMsg);
}
else
{
OnWebServiceFailed(null);
}
}
catch (Exception ex)
{
//Web Service failed...
OnWebServiceFailed(ex);
}
return null;
}
}
}
-
-
II - Parsing the Json data
-
Once we retrieved our Json message from the cloud, we still need to parse it in an usable format. At that point it is very convenient to rely on one of the numerous Json parsing SDK freely available on the web. As I mentioned in the previous post, you will be able to check the existing libs from the www.json.org webpage.
-
I chose the one from Google, mainly because it appeared to be one of the most friendly and was also reported as having good processing delays: it is called Gson.
-
In order to parse a Json message with Gson you need to create a custom class and provide it to the parser in order to get your class instance or array of instances directly extracted for you.
-
Here is the code I placed in my web service success callback, you can see it is very straightforward:
-
public class ModelInfo
{
public int Height;
public int Width;
public String FileExt;
public String ModelId;
}
-
public void OnWebServiceSuccess(String JsonMsg)
{
final Activity activity = this;
try
{
Type collectionType = new TypeToken<ModelInfo[]>(){}.getType();
-
// invokes our Gson parser created in the constructor
final ModelInfo[] models = (ModelInfo[])
_parser.fromJson(JsonMsg , collectionType);
-
this.runOnUiThread(new Runnable()
{
public void run()
{
// exploit results: displays models in ListView UI
}
});
}
catch (Exception e)
{
OnWebServiceFailed(activity);
return;
}
}
-
One thing to pay attention to: when adding .jar from a third party library, take care about checking them in the “Order and Export” tab of the “Java Build Path” as illustrated below. It basically tells the compiler to include them in your package, so they are available when deployed on the device:
-
-
III – Putting it together
-
The last step was mainly to put it all together, so I created a main activity with a very simple UI that contains only an ImageView in order to display our cloud pictures, and a second activity that is responsible for displaying the available cloud models in a custom ListView. Each activity performs its own webservice call, one for retrieving models and one for retrieving data for a specific selected model.
-
The last thing to care about is to specify the INTERNET permission in the manifest and the existence of the second activity:
-
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="Autodesk.ADN.ViewerTut"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="10"/>
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".AdnViewerTutActivity"
android:label="@string/app_name"
android:configChanges="orientation|keyboardHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ModelSelectActivity"
android:label="@string/app_name"
android:configChanges="orientation|keyboardHidden"/>
</application>
</manifest>
-
And here is the astonishing result!
-
-
The model selection activity:
-
-
And a selected model displayed:
-
-
You will find the whole project attached. In the next post I will dig a bit deeper this Android app in order to add touch event support, so the user will be able to zoom and pan the picture with his chubby fingers.
Comments