Calling a Web Service from Android

Spanish

This document describes how to connect to a Web service from an Android application. It is assumed that the reader has basic knowledge of the Android OS, as well as Web services and SOAP messages. If you require further information, you can browse the following links:

http://developer.android.com/index.html
http://www.w3schools.com/webservices/default.asp
http://www.w3schools.com/soap/default.asp

The application described below is divided into 2 parts: the code required to connect to a Web service and the test cases to verify its functionality.

The following libraries and applications are used:

– Windows 7

– Eclipse Galileo (Android DDMS, Android Development Tools)

Ksoap2-androidlibrary

The application aims to connect to a Web service, which converts from Fahrenheit to Celsius. The details of this service and the WSDL is available at the following URL:

http://www.w3schools.com/webservices/tempconvert.asmx

1.1) First, create an Android project. If the plugin was properly installed, you should see “Android Project” listed as one of the options available:

image_thumb

1.2) You can put any name for the project, in my case, I called it webServiceSample. The android version specified is 2.3, but the code should work correctly on earlier versions. In the “Properties” section, you specify the application name, package name and the activity name. These fields were filled out as follows: Web Service Sample, com.webservice.test, WSMain.

image_thumb1[1]

If you want to create an Android test project, click on NEXT, otherwise select FINISH. To create a test project, you have to check the “create a Test Project” option and Eclipse will automatically populate all the required fields.

image_thumb2

1.3) Eclipse automatically creates a skeleton and updates the AndroidManifest file. Next, you must update the application layout to contain an edit box and a button. To achieve this, you can use the graphical layout editor or modify the XML file directly. The layout looks like this:

image_thumb3

Code:

<?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:layout_width="fill_parent"
	    android:layout_height="wrap_content"
	    android:text="Fahrenheit: "
    />
    <EditText
    	android:id="@+id/inputTemp"
    	android:layout_width="fill_parent"
	    android:layout_height="wrap_content"
	    android:inputType="numberSigned"
	    android:background="@android:drawable/editbox_background"
	    />
    <Button
    	android:layout_width="wrap_content"
    	android:text="Convert"
    	android:layout_height="wrap_content"
    	android:id="@+id/convert"
   	/>
	<TextView
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:id="@+id/response"
		android:gravity="center_horizontal"
	/>

</LinearLayout>

1.4) Before start coding the main activity, you must import the ksoap2 library. To do this, you choose configure build path and add an external JAR:

clip_image002_thumb

1.5) Open the main activity (WSMain) and declare the components of the SOAP message (namespace, URL, soap action, method name):

public class WSMain extends Activity {

	private final String NAMESPACE = "http://tempuri.org/";
	private final String URL = "http://www.w3schools.com/webservices/tempconvert.asmx";
	private final String SOAPACTION = "http://tempuri.org/FahrenheitToCelsius";
	private final String METHOD = "FahrenheitToCelsius";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

    }

....

These values are taken from the Web Service definition:

POST /webservices/tempconvert.asmx HTTP/1.1
Host: www.w3schools.com
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://tempuri.org/FahrenheitToCelsius"

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <FahrenheitToCelsius xmlns="http://tempuri.org/">
      <Fahrenheit>string</Fahrenheit>
    </FahrenheitToCelsius>
  </soap:Body>
</soap:Envelope>

1.6) The Web service call is made through a public function called convertFahrenheitToCelsius which receives as parameter a View object (the reason is explained below).

Before building the SOAP message, we need a reference of the TextView objects which hold the input/output values:

public void convertFahrenheitToCelsius(View  v)
    {
    	TextView response_tv = (TextView)findViewById(id.response);
    	final TextView input_tv = (TextView)findViewById(id.inputTemp);
    	String fahrenheit_value = input_tv.getText().toString();

    	if(fahrenheit_value == null || fahrenheit_value.length() == 0)
    	{
    		response_tv.setText("Error!");
    		return;
    	}

....

Variable fahrenheit_value stores the degrees Fahrenheit you want to convert. After they are stored, the variable is evaluated to verify it is not empty. This step is not needed, but I have included it to cover this scenario in the test case.

1.7) The next step is to create the SOAP message using the SOAPObject class. This object will hold information like the method name, input value, etc..

...
SoapObject request = new SoapObject(NAMESPACE, METHOD);

        PropertyInfo FahrenheitProp = new PropertyInfo();
        FahrenheitProp.setName("Fahrenheit");
        FahrenheitProp.setValue(fahrenheit_value);
        FahrenheitProp.setType(String.class);
        request.addProperty(FahrenheitProp);

        SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
        envelope.dotNet = true;
        envelope.setOutputSoapObject(request);
...

NOTE: the envelope.dotNet property is set to “true” because the target Web service is coded in . NET.

1.8) Once the SOAP message is built, you must connect to the Web service. This is done through an HTTP request:

...
HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
        try
        {
        	androidHttpTransport.call(SOAPACTION, envelope);
        	SoapPrimitive response = (SoapPrimitive)envelope.getResponse();
        	response_tv.setText(fahrenheit_value + " F = " + response.toString() + " C");

        }catch(Exception e)
        {
        	e.printStackTrace();
        }
...

In this last piece of code, the request to the web service is made; and similarly the response is received.

1.9) The final code looks like the following:

package com.webservice.test;

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.PropertyInfo;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapPrimitive;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;

import com.webservice.test.R.id;

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

public class WSMain extends Activity {

	private final String NAMESPACE = "http://tempuri.org/";
	private final String URL = "http://www.w3schools.com/webservices/tempconvert.asmx";
	private final String SOAPACTION = "http://tempuri.org/FahrenheitToCelsius";
	private final String METHOD = "FahrenheitToCelsius";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

    }

    public void convertFahrenheitToCelsius(View  v)
    {
    	TextView response_tv = (TextView)findViewById(id.response);
    	final TextView input_tv = (TextView)findViewById(id.inputTemp);
    	String fahrenheit_value = input_tv.getText().toString();

    	if(fahrenheit_value == null || fahrenheit_value.length() == 0)
    	{
    		response_tv.setText("Error!");
    		return;
    	}

    	SoapObject request = new SoapObject(NAMESPACE, METHOD);

        PropertyInfo FahrenheitProp = new PropertyInfo();
        FahrenheitProp.setName("Fahrenheit");
        FahrenheitProp.setValue(fahrenheit_value);
        FahrenheitProp.setType(String.class);
        request.addProperty(FahrenheitProp);

        SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
        envelope.dotNet = true;
        envelope.setOutputSoapObject(request);
        HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
        try
        {
        	androidHttpTransport.call(SOAPACTION, envelope);
        	SoapPrimitive response = (SoapPrimitive)envelope.getResponse();
        	response_tv.setText(fahrenheit_value + " F = " + response.toString() + " C");

        }catch(Exception e)
        {
        	e.printStackTrace();
        }
    }
}

1.10) At this point we have all the necessary to communicate with the Web service. However, if we run the application it will fail, mainly for 2 reasons: convertFahrenheitToCelsius function is never invoked and the application is not authorized to browse the Internet.

First problem is solved updating the application layout. The following tag must be added into Button tag:

android: onClick = “convertFahrenheitToCelsius”

...
<Button
    	android:layout_width="wrap_content"
    	android:text="Convert"
    	android:layout_height="wrap_content"
    	android:id="@+id/convert"
    	android:onClick="convertFahrenheitToCelsius"
   	/>
...

The description for the Button component says that the OnClick property must call a PUBLIC function which receive as a parameter a VIEW object.

The second constraint is solved by editing the AndroidManifest.xml and adding the following tag:

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

&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
      package=&quot;com.webservice.test&quot;
      android:versionCode=&quot;1&quot;
      android:versionName=&quot;1.0&quot;&gt;
    &lt;application android:icon=&quot;@drawable/icon&quot; android:label=&quot;@string/app_name&quot;&gt;
        &lt;activity android:name=&quot;.WSMain&quot;
                  android:label=&quot;@string/app_name&quot;&gt;
            &lt;intent-filter&gt;
                &lt;action android:name=&quot;android.intent.action.MAIN&quot; /&gt;
                &lt;category android:name=&quot;android.intent.category.LAUNCHER&quot; /&gt;
            &lt;/intent-filter&gt;
        &lt;/activity&gt;
    &lt;/application&gt;
&lt;uses-permission android:name=&quot;android.permission.INTERNET&quot;&gt;&lt;/uses-permission&gt;
&lt;/manifest&gt;

1.11) If the application is executed, you must see the following:

image_thumb1

If the correct output is not shown, you can use DDMS perspective to debug the code.

2.1) The functionality can be verified through JUnit and the test project  created before (webServiceSampleTest). A JUnit Test Case file must be added into the test project:

image_thumb[1]

The test case name is InputValidation. The super class extends the ActivityInstrumentationTestCase2 class. And class that is under test is android.test.AndroidTestCase.

In addition, the setUp () and constructor methods are checked. For more information on these methods, the following tutorial can be read:

http://mobile.tutsplus.com/tutorials/android/android-sdk-junit-testing/

2.2) The test case will evaluate three basic conditions: a positive input value, negative input value and empty value. Therefore, we first declare the expected outputs for these scenarios:

public class InputValidation extends ActivityInstrumentationTestCase2 {

	private TextView result;

	//results
	private static final String FAHR_50 = &quot;50 F = 10 C&quot;;
	private static final String FAHR_N67 = &quot;-67 F = -55 C&quot;;
	private static final String EMPTYVALUE = &quot;Error!&quot;;
...

2.3) Next step is to update the setUp and constructor methods. In the constructor, the only thing to do is to call the super class. For the setUp method, a reference to the main activity and the TextView objects is defined.

...
public InputValidation() {
		super(&quot;com.webservice.test&quot;, WSMain.class);
	}

	protected void setUp() throws Exception {
		super.setUp();
		WSMain mainActivity = getActivity();
		result = (TextView)mainActivity.findViewById(id.response);
	}
...

2.4) For the first scenario, a positive input value is given. 50 degrees Fahrenheit will be converted to Celsius, so the expected value is 10:

...
public void testPositiveValue()
	{
		sendKeys(&quot;5 0 ENTER &quot;); // 50 Fahrenheit
		sendKeys(&quot;ENTER&quot;);

		//result
		String celsius_result = result.getText().toString();
		assertTrue(celsius_result, celsius_result.equals(FAHR_50));
	}
...

2.5) The second test case passes a negative value. –67 degrees Fahrenheit will be converted to Celsius, the expected output is -55:

...
	public void testNegativeValue()
	{
		sendKeys(&quot;MINUS 6 7 ENTER&quot;); // -67 Fahrenheit
		sendKeys(&quot;ENTER&quot;);

		//result
		String celsius_result = result.getText().toString();
		assertTrue(celsius_result, celsius_result.equals(FAHR_N67));
	}
...

2.6) Last scenario will test the empty string. An empty value is given as input and the application should print the message: “Error!”.

...
	public void testEmptyValue()
	{
		sendKeys(&quot;ENTER ENTER&quot;);
		String celsius_result = result.getText().toString();
		assertTrue(celsius_result, celsius_result.equals(EMPTYVALUE));
	}
...

2.7) Complete code should look like this:

package com.webservice.test.test;

import com.webservice.test.WSMain;
import com.webservice.test.R.id;

import android.test.ActivityInstrumentationTestCase2;
import android.widget.TextView;

public class InputValidation extends ActivityInstrumentationTestCase2 {

	private TextView result;

	//results
	private static final String FAHR_50 = &quot;50 F = 10 C&quot;;
	private static final String FAHR_N67 = &quot;-67 F = -55 C&quot;;
	private static final String EMPTYVALUE = &quot;Error!&quot;;

	public InputValidation() {
		super(&quot;com.webservice.test&quot;, WSMain.class);
	}

	protected void setUp() throws Exception {
		super.setUp();
		WSMain mainActivity = getActivity();
		result = (TextView)mainActivity.findViewById(id.response);
	}

	public void testPositiveValue()
	{
		sendKeys(&quot;5 0 ENTER &quot;); // 50 Fahrenheit
		sendKeys(&quot;ENTER&quot;);

		//result
		String celsius_result = result.getText().toString();
		assertTrue(celsius_result, celsius_result.equals(FAHR_50));
	}

	public void testNegativeValue()
	{
		sendKeys(&quot;MINUS 6 7 ENTER&quot;); // -67 Fahrenheit
		sendKeys(&quot;ENTER&quot;);

		//result
		String celsius_result = result.getText().toString();
		assertTrue(celsius_result, celsius_result.equals(FAHR_N67));
	}

	public void testEmptyValue()
	{
		sendKeys(&quot;ENTER ENTER&quot;);
		String celsius_result = result.getText().toString();
		assertTrue(celsius_result, celsius_result.equals(EMPTYVALUE));
	}
}

2.8) To launch the JUnit test, we must right click on the file and select DebugInputValidation.java As -> Android JUnit test

image_thumb[2]

.9) The test cases must run successfully:

image_thumb[3]

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: