Sie sind auf Seite 1von 15

Acceso a Servicios Web SOAP en Android (2/2)

por Sgoliver el 27/02/2012 en Android, Programacin

En el artculo anterior del curso vimos cmo construir un servicio web SOAP haciendo uso de ASP.NET y una base de datos externa SQL Server. En este segundo artculo veremos cmo podemos acceder a este servicio web desde una aplicacin Android y probaremos todo el sistema en local para verificar su correcto funcionamiento. En primer lugar hay que empezar diciendo que Android no incluye de serie ningn tipo de soporte para el acceso a servicios web de tipo SOAP. Es por esto por lo que vamos a utilizar una librera externa para hacernos ms fcil esta tarea. Entre la oferta actual, la opcin ms popular y ms utilizada es la librera ksoap2-android. Esta librera es un fork, especialmente adaptado para Android, de la antigua librera kSOAP2. Este framework nos permitir de forma relativamente fcil y cmoda utilizar servicios web que utilicen el estndar SOAP. La ltima versin de esta librera en el momento de escribir este artculo es la 2.6.0, que puede descargarse desde este enlace. Agregar esta librera a nuestro proyecto Android es muy sencillo. Si tenemos una versin reciente del plugin de Android para Eclipse, una vez tenemos creado el proyecto en Android bastar con copiar el archivo .jar en la carpeta libs de nuestro proyecto. Si tu versin del plugin es ms antigua es posible que tengas que adems aadir la librera al path del proyecto. Para ello accederemos al men Project / Properties y en la ventana de propiedades accederemos a la seccin Java Build Path. En esta seccin accederemos a la solapa Libraries y pulsaremos el botn Add External JARs. Aqu seleccionamos el fichero jar de la librera ksoap2-android (en este caso ksoap2-android-assembly-3.6.0-jar-withdependencies.jar) y listo, ya tenemos nuestro proyecto preparado para hacer uso de la funcionalidad aportada por la librera. Como aplicacin de ejemplo, vamos a crear una aplicacin sencilla que permita aadir un nuevo usuario a la base de datos. Para ello aadiremos a la vista principal dos cuadros de texto para introducir el nombre y telfono del nuevo cliente (en mi caso se llamarntxtNombre y txtTelefono respectivamente) y un botn (en mi caso btnEnviar) que realice la llamada al mtodo NuevoCliente del servicio web pasndole como parmetros los datos introducidos en los cuadros de texto anteriores. No voy a mostrar todo el cdigo necesario para crear esta vista y obtener las referencias a cada control porque no tiene ninguna particularidad sobre lo ya visto en multitud de ocasiones en artculos anteriores del curso (en cualquier caso al final del artculo podis descargar todo el cdigo fuente para su consulta). Lo que nos interesa en este caso es la implementacin del evento onClick del botn btnEnviar, que ser el encargado de comunicarse con el servicio web y procesar el resultado. Lo primero que vamos a hacer en este evento es definir, por comodidad, cuatro constantes que nos servirn en varias ocasiones durante el cdigo:

NAMESPACE. Espacio de nombres utilizado en nuestro servicio web. URL. Direccin URL para realizar la conexin con el servicio web. METHOD_NAME. Nombre del mtodo web concreto que vamos a ejecutar. SOAP_ACTION. Equivalente al anterior, pero en la notacin definida por SOAP.

Aunque los valores se podran ms o menos intuir, para conocer exactamente los valores que debemos asignar a estas constantes vamos a ejecutar una vez ms el proyecto de Visual Studio que construimos en el artculo anterior y vamos a acceder a la pgina de prueba del mtodo NuevoCliente. Veremos algo parecido a lo siguiente:

En la imagen anterior se muestran resaltados en rojo los valores de las cuatro constantes a definir, que en nuestro caso concreto quedaran de la siguiente forma:

1 2 3 4

String NAMESPACE = "http://sgoliver.net/"; String URL="http://10.0.2.2:1473/ServicioClientes.asmx"; String METHOD_NAME = "NuevoClienteSimple"; String SOAP_ACTION = "http://sgoliver.net/NuevoClienteSimple";

Como podis comprobar, y esto es algo importante, en la URL he sustituido el nombre de mquina localhost por su direccin IP equivalente, que en el caso de aplicaciones Android ejecutadas en el emulador se corresponde con la direccin 10.0.2.2, en vez de la clsica127.0.0.1. Adems debes verificar que el puerto coincide con el que ests utilizando en tu mquina. En mi caso el servicio se ejecuta sobre el puerto 1473, pero es posible que en tu caso el nmero sea distinto. Los siguientes pasos del proceso sern crear la peticin SOAP al servicio web, enviarla al servidor y recibir la respuesta. Aunque ya dijimos que todo este proceso sera casi transparente para el programador, por ser sta la primera vez que hablamos del tema me voy a detener un poco ms para intentar que entendamos lo que estamos haciendo y no solo nos limitemos a copiar/pegar trozos de cdigo que no sabemos lo que hacen.

Volvamos a la pgina de prueba del mtodo web NuevoCliente. Justo debajo de la seccin donde se solicitan los parmetros a pasar al mtodo se incluye tambin un XML de muestra de cmo tendra que ser nuestra peticin al servidor si tuviramos que construirla a mano. Echmosle un vistazo:

Una vez ms he marcado varias zonas sobre la imagen, correspondientes a las tres partes principales de una peticin de tipo SOAP. Empezando por la parte interna del XML, en primer lugar encontramos los datos de la peticin en s (Request) que contiene el nombre del mtodo al que queremos llamar, y los nombres y valores de los parmetros en entrada. Rodeando a esta informacin se aaden otra serie de etiquetas y datos a modo decontenedor estndar que suele recibir el nombre de Envelope. La informacin indicada en este contenedor no es especfica de nuestra llamada al servicio, pero s contiene informacin sobre formatos y esquemas de validacin del estndar SOAP. Por ltimo, durante el envo de esta peticin SOAP al servidor mediante el protocolo HTTP se aaden determinados encabezados como los que veis en la imagen. Todo esto junto har que el servidor sea capaz de interpretar correctamente nuestra peticin SOAP, se llame al mtodo web correcto, y se devuelva el resultado en un formato similar al anterior que ya veremos ms adelante. Aclarada un poco la estructura y funcionamiento general de una peticin SOAP veamos lo sencillo que resulta realizarla desde nuestra aplicacin Android. En primer lugar crearemos la peticin (request) a nuestro mtodo NuevoCliente. Para ello crearemos un nuevo objeto SoapObject pasndole el

En primer lugar crearemos la peticin (request) a nuestro mtodo NuevoCliente. Para ello crearemos un nuevo objeto SoapObject pasndole el namespace y el nombre del mtodo web. A esta peticin tendremos que asociar los parmetros de entrada mediante el mtodoaddProperty() al que pasaremos los nombres y valores de los parmetros (que en nuestro caso se obtendrn de los cuadros de texto de la vista principal).

1 2 3 4

SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME); request.addProperty("nombre", txtNombre.getText().toString()); request.addProperty("telefono", txtTelefono.getText().toString());

El segundo paso ser crear el contenedor SOAP (envelope) y asociarle nuestra peticin. Para ello crearemos un nuevo objeto SoapSerializationEnvelope indicando la versin de SOAP que vamos a usar (versin 1.1 en nuestro caso, como puede verse en la imagen anterior). Indicaremos adems que se trata de un servicio web .NET activando su propiedad dotNet. Por ltimo, asociaremos la peticin antes creada a nuestro contenedor llamando al mtodosetOutputSoapObject().

1 2 3

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

4 5 6

envelope.setOutputSoapObject(request);

Como tercer paso crearemos el objeto que se encargar de realizar la comunicacin HTTP con el servidor, de tipo HttpTransportSE, al que pasaremos la URL de conexin a nuestro servicio web. Por ltimo, completaremos el proceso realizando la llamada al servicio web mediante el mtodo call().

1 2 3 4 5 6 7 8 9 10 11 12 13

HttpTransportSE transporte = new HttpTransportSE(URL); try { transporte.call(SOAP_ACTION, envelope); //Se procesa el resultado devuelto //... } catch (Exception e) { txtResultado.setText("Error!"); }

Tras la llamada al servicio ya estamos en disposicin de ob


obtener el resultado devuelto por el mtodo web llamado. Esto lo conseguimos mediante el mtodo getResponse(). Dependiendo del tipo de resultado que esperemos recibir deberemos convertir esta respuesta a un tipo u otro. En este caso, como el resultado que esperamos es un valor simple (un nmero entero) convertiremos la respuesta a un objeto SoapPrimitive, que directamente podremos convertir a una cadena de caracteres llamado a toString(). Ms adelante veremos cmo tratar valores de retorno ms complejos.

1 2 3 4 5

SoapPrimitive resultado_xml =(SoapPrimitive)envelope.getResponse(); String res = resultado_xml.toString(); if(res.equals("1")) txtResultado.setText("Insertado OK");

Y listo, con esto ya tenemos preparada la llamada a nuestro servicio web y el tratamiento de la respuesta recibida. Un detalle ms antes de poder probar todo el sistema. Debemos acordarnos de conceder permiso de acceso a internet a nuestra aplicacin, aadiendo la linea correspondiente alAndroid Manifest:

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

Pues bien, para probar lo que llevamos hasta ahora podemos ejecutar ambos proyectos simultneamente, en primer lugar el de Visual Studio para iniciar la ejecucin del servidor local que alberga nuestro servicio web (hay que dejar abierto el explorador una vez que se abra), y posteriormente el de Eclipse para iniciar nuestra aplicacin Android en el Emulador. Una vez estn los dos proyectos en ejecucin, podemos rellenar los datos de nuestro cliente en la aplicacin Android y pulsar el botn Enviar para realizar la llamada al servicio web e insertar el cliente en la base de datos (que por supuesto tambin deber estar iniciada). Si todo va bien y no se produce ningn error, deberamos poder consultar la tabla de Clientes a travs del SQL Server Management Studio para verificar que el cliente se ha insertado correctamente.

En la imagen vemos cmo hemos insertado un nuevo cliente llamada cliente7 con nmero de telfono 7777. Si consultamos ahora nuestra base de datos Sql Server podremos comprobar si el registro efectivamente se ha insertado correctamente.

Algo importante que quiero remarcar llegados a este punto. El cdigo anterior debe funcionar correctamente sobre un dispositivo o emulador con versin de Android anterior a la 3.0. Sin embargo, si intentamos ejecutar la aplicacin sobre una versin posterior obtendremos una excepcin de tipo NetworkOnMainThread. Esto es debido a que en versiones recientes de Android no se permite realizar operaciones de larga duracin directamente en el hilo principal de la aplicacin. Para solucionar esto y que nuestra aplicacin funcione bajo cualquier versin de Android ser necesario trasladar el cdigo que hemos escrito para llamar al servicio web a una AsyncTask que realice las operaciones en segundo plano utilizando un hilo secundario. El curso contiene un capitulo dedicado a describir con ms detalle las tareas asncronas oAsyncTask, por lo que en este caso me limitar a poner cmo quedara nuestro cdigo dentro de la AsyncTask.

1 //Tarea Asncrona para llamar al WS de consulta en segundo plano 2 private class TareaWSConsulta extends 3 AsyncTask<String,Integer,Boolean> { 4 private Cliente[] listaClientes; 5 6 protected Boolean doInBackground(String... params) { 7 8 boolean resul = true; 9 1 final String NAMESPACE = "http://sgoliver.net/"; final String URL="http://10.0.2.2:1473/ServicioClientes.asmx"; 0 final String METHOD_NAME = "ListadoClientes"; 1 final String SOAP_ACTION = "http://sgoliver.net/ListadoClientes"; 1 1

SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME); 2 1 SoapSerializationEnvelope envelope = 3 new SoapSerializationEnvelope(SoapEnvelope.VER11); 1 envelope.dotNet = true; 4 1 envelope.setOutputSoapObject(request); 5 HttpTransportSE transporte = new HttpTransportSE(URL); 1 6 try 1 { 7 transporte.call(SOAP_ACTION, envelope); 1 8 SoapObject resSoap =(SoapObject)envelope.getResponse(); 1 9 listaClientes = new Cliente[resSoap.getPropertyCount()]; 2 for (int i = 0; i < listaClientes.length; i++) 0 { 2 SoapObject ic = (SoapObject)resSoap.getProperty(i); 1 2 Cliente cli = new Cliente(); 2 cli.id = 2 Integer.parseInt(ic.getProperty(0).toString()); cli.nombre = ic.getProperty(1).toString(); 3 cli.telefono = 2 Integer.parseInt(ic.getProperty(2).toString( 4 )); 2 5 listaClientes[i] = cli; } 2 } 6 catch (Exception e) 2 { 7 resul = false; 2 } 8 return resul; 2 } 9 3 protected void onPostExecute(Boolean result) { 0 3 if (result) 1 { //Rellenamos la lista con los nombres de los clientes 3 final String[] datos = new String[listaClientes.length]; 2 3 for(int i=0; i<listaClientes.length; i++) 3 datos[i] = listaClientes[i].nombre; 3 4 ArrayAdapter<String> adaptador = 3 new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, datos); 5 3 lstClientes.setAdapter(adaptador); 6 } 3 else

7 3 8 3 } 9 4 0 4 1 4 2 4 3 4 4 4 5 4 6 4 7 4 8 4 9 5 0 5 1 5 2 5 3 5 4 5 5 5 6 5 7 5 8 5 9 6 0 6 1 6

{ txtResultado.setText("Error!"); } }

2 6 3 6 4 6 5 6 6 6 7 6 8 6 9 7 0 7 1 7 2 7 3 7 4 7 5
Como podemos ver, prcticamente todo el cdigo se ha trasladado al mtododoInBackground() de la tarea, salvo la parte en la que debemos actualizar la interfaz de usuario tras la llamada que debe ir al mtodo onPostExecute(). Por su parte, una vez creada la tarea asncrona, en el evento click del botn nos limitaremos a instanciar la tarea y ejecutarla llamando a su mtodo execute().

1 2 3 4 5 6 7 8

btnConsultar.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { TareaWSConsulta tarea = new TareaWSConsulta(); tarea.execute(); } });

Con esto, ya sabemos realizar una llamada a un servicio web SOAP que devuelve un valor de retorno sencillo, en este caso un simple nmero entero. Lo siguiente que vamos a ver ser como implementar la llamada a un mtodo del servicio web que nos devuelva un valor algo ms complejo. Y esto lo vamos a ver con la llamada al mtodo web ListadoClientes() que recordemos devolva un array de objetos de tipo Cliente. En este caso, la llamada al mtodo web se realizar de forma totalmente anloga a la ya comentada. Donde llegarn las diferencias ser a la hora de tratar el resultado devuelto por el servicio, comenzando por el resultado del mtodo getResponse() de ksoap. En esta ocasin, dado que el resultado esperado no es ya un valor simple sino un objeto ms complejo, convertiremos el resultado de getResponse() al tipo SoapObject, en vez deSoapPrimitive como hicimos anteriormente. Nuestro objetivo ser generar

un array de objetos Cliente (lo llamaremos listaClientes) a partir del resultado devuelto por la llamada al servicio. Como sabemos que el resultado devuelto por el servicio es tambin un array, lo primero que haremos ser crear un array local con la misma longitud que el devuelto, lo que conseguiremos mediante el mtodo getPropertyCount(). Tras esto, iteraremos por los distintos elementos del array devuelto mediante el mtodo getProperty(ind), donde indser el ndice de cada ocurrencia. Cada uno de estos elementos ser a su vez otro objeto de tipo SoapObject, que representar a un Cliente. Adicionalmente, para cada elemento accederemos a sus propiedades (Id, Nombre, y Telefono) una vez ms mediante llamadas agetProperty(), con el ndice de cada atributo, que seguir el mismo orden en que se definieron. As, getProperty(0) recuperar el Id del cliente, getProperty(1) el nombre, ygetProperty(2) el telfono. De esta forma podremos crear nuestros objetos Clientelocales a partir de estos datos. Al final de cada iteracin aadimos el nuevo cliente recuperado a nuestro array. Veamos como quedara todo esto en el cdigo, donde seguro que se entiende mejor:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

SoapObject resSoap =(SoapObject)envelope.getResponse(); Cliente[] listaClientes = new Cliente[resSoap.getPropertyCount()]; for (int i = 0; i < listaClientes.length; i++) { SoapObject ic = (SoapObject)resSoap.getProperty(i); Cliente cli = new Cliente(); cli.id = Integer.parseInt(ic.getProperty(0).toString()); cli.nombre = ic.getProperty(1).toString(); cli.telefono = Integer.parseInt(ic.getProperty(2).toString()); listaClientes[i] = cli; }

n nuestra aplicacin de ejemplo aadimos un nuevo botn y un control tipo lista (lo llamo lstClientes), de forma que al pulsar dicho botn rellenemos la lista con los nombres de todos los clientes recuperados. La forma de rellenar una lista con un array de elementos ya la vimos en los artculos dedicados a los controles de seleccin, por lo que no nos pararemos a comentarlo. El cdigo sera el siguiente (Nota: s que todo esto se podra realizar de forma ms eficiente sin necesidad de crear distintos arrays para los clientes y para el adaptador de la lista, pero lo dejo as para no complicar el tutorial con temas ya discutidos en otros artculos):

1 2 3 4 5 6 7 8

//Rellenamos la lista con los nombres de los clientes final String[] datos = new String[listaClientes.length]; for(int i=0; i<listaClientes.length; i++) datos[i] = listaClientes[i].nombre; ArrayAdapter<String> adaptador = new ArrayAdapter<String>(ServicioWebSoap.this, android.R.layout.simple_list_item_1, datos);

9 10 11

lstClientes.setAdapter(adaptador);

Por ltimo, vamos a ver cmo llamar a un mtodo web que recibe como parmetro algn objeto complejo. Para ilustrarlo haremos una llamada al segundo mtodo de insercin de clientes que implementamos en el servicio, NuevoClienteObjeto(). Recordemos que este mtodo reciba como parmetro de entrada un objeto de tipo Cliente. Para poder hacer esto, lo primero que tendremos que hacer ser modificar un poco nuestra clase Cliente, de forma que ksoap sepa cmo serializar nuestros objetos Cliente a la hora de generar las peticiones SOAP correspondientes. Y para esto, lo que haremos ser implementar la interfaz KvmSerializable en nuestra clase Cliente. Para ello, adems de aadir la clusula implements correspondiente tendremos que implementar los siguientes mtodos: getProperty(int indice) getPropertyCount() getPropertyInfo(int indice, HashTable ht, PropertyInfo info) setProperty(int indice, Object valor) El primero de ellos deber devolver el valor de cada atributo de la clase a partir de su ndice de orden. As, para el ndice 0 se devolver el valor del atributo Id, para el ndice 1 el del atributo Nombre, y para el 2 el atributo Telfono.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

@Override public Object getProperty(int arg0) { switch(arg0) { case 0: return id; case 1: return nombre; case 2: return telefono; } return null; }

El segundo de los mtodos, deber devolver simplemente el nmero de atributos de nuestra clase, que en nuestro caso ser 3 (Id, Nombre y Telefono):

1 2 3 4

@Override public int getPropertyCount() { return 3; }

El objetivo del tercero ser informar, segn el ndice recibido como parmetro, el tipo y nombre del atributo correspondiente. El tipo de cada atributo se devolver como un valor de la clase PropertyInfo.

1 2 3 4 5 6 7

@Override public void getPropertyInfo(int ind, Hashtable ht, PropertyInfo info) { switch(ind) { case 0: info.type = PropertyInfo.INTEGER_CLASS; info.name = "Id"; break;

8 9 10 11 12 13 14 15 16 17 18 19 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

case 1: info.type = info.name = break; case 2: info.type = info.name = break; default:break; } }

PropertyInfo.STRING_CLASS; "Nombre"; PropertyInfo.INTEGER_CLASS; "Telefono";

Por ltimo, el mtodo setProperty() ser el encargado de asignar el valor de cada atributo segn su ndice y el valor recibido como parmetro.

@Override public void setProperty(int ind, Object val) { switch(ind) { case 0: id = Integer.parseInt(val.toString()); break; case 1: nombre = val.toString(); break; case 2: telefono = Integer.parseInt(val.toString()); break; default: break; } }

Mediante estos mtodos, aunque de forma transparente para el programados, ksoap ser capaz de transformar nuestros objetos Cliente al formato XML correcto de forma que pueda pasarlos como parmetro en las peticiones SOAP a nuestro servicio. Por su parte, la llamada al servicio tambin difiere un poco de lo ya comentado a la hora de asociar los parmetros de entrada del mtodo web. En este caso, construiremos en primer lugar el objeto Cliente que queremos insertar en la base de datos a partir de los datos introducidos en la pantalla de nuestra aplicacin de ejemplo. Tras esto crearemos un nuevo objeto PropertyInfo, al que asociaremos el nombre, valor y tipo de nuestro cliente mediante sus mtodos setName(), setValue() y setClass() respectivamente. Por ltimo, asociaremos este cliente como parmetro de entrada al servicio llamando al metodo addProperty() igual que hemos hecho en las anteriores ocasiones, con la diferencia de que esta vez lo llamaremos pasndole el objeto PropertyInfo que acabamos de crear. Adems de esto, tendremos tambin que llamar finalmente al mtodo addMapping() para asociar de alguna forma nuestro espacio de nombres y nombre de clase Cliente con la clase real java. Veamos el cdigo para entenderlo mejor:

1 2 3 4 5 6

Cliente cli = new Cliente(); cli.nombre = txtNombre.getText().toString(); cli.telefono = Integer.parseInt(txtTelefono.getText().toString()); PropertyInfo pi = new PropertyInfo(); pi.setName("cliente");

7 8 9 10 11 12 13 14 15 16 17 18

pi.setValue(cli); pi.setType(cli.getClass()); request.addProperty(pi); SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); envelope.dotNet = true; envelope.setOutputSoapObject(request); envelope.addMapping(NAMESPACE, "Cliente", cli.getClass());

Todo esto lo haremos en un nuevo botn aadido a la aplicacin de ejemplo ( Enviar2), cuyo efecto tendr que ser idntico al que ya creamos para la llamada al mtodo webNuevoClienteSimple(), aunque como acabamos de ver su implementacin es algo diferente debido a los distintos parmetros de entrada utilizados. Como imagen final veamos una captura de la pantalla final de nuestra aplicacin de ejemplo, donde vemos los tres botones implementados, junto al resultado de la ejecucin de cada uno, el mensaje Insertado OK de los mtodos de insercin, y la lista de clientes recuperada por el mtodo de consulta.

http://www.sgoliver.net/blog/?p=2594

http://jcalderon.wordpress.com/2008/02/25/subir-una-aplicacion-web-java-en-un-servidor/

public class ConexionBd {

public ConexionBd() { super(); } }

http://s156.eatj.com/myacct.jsp

<activity android:name="com.fs.app1.Calculadora" android:label="Calculadora" > <intent-filter> <action android:name="com.fs.app1.CALCULADORA" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>

Das könnte Ihnen auch gefallen