sábado, 30 de noviembre de 2013

Consumir Web Services Soap en Android (2/2)

Consumir Web Services Soap en Android (parte 2)

En esta segunda parte vamos a consumir el Servicio Web que hicimos en .Net en la parte 1. Utilizaremos soap para poder establecer la comunicación con el servicio y poder realizar las transacciones.

Antes de empezar con la segunda parte veamos de que se compone un objeto soap:
soap object

Requisitos para realizar este tutorial:
Empezamos:

1. Importar las librerias ksoap2 que se descargaron de la siguiente manera:

  1. Click derecho sobre el proyecto, escoger Build Path/Configure buld path/Libreries/Add externals Jars y escoger el archivo .jar que contiene la libreria ksoap2. 
  2. Luego ir a Order and Export, dar check a la libreria ksoap y finalmente click en OK.




2. Crear un proyecto nuevo en eclipse con nombre "VisitasConexionWebService", agregar 2 layouts con nombres activity_main.xml y consultar_registros.xml

layout main
activity_main.xml
layout de consultas
consultar_registros.xml




















Nota: No se explica como diseñar los layouts porque es irrelevante para este tutorial, el código fuente de los xml se dará al final en un enlace de descarga.

Básicamente activity_main.xml tiene 4 spinners (comboBox), 2 EditText y 2 botones.
consultar_registros.xml tiene 2 EditText, 2 botones y 1 ListView.

Soap para consultar
3. Crear una clase "Vendedor.java"

package com.example.visitasconexionwebservice;

import java.util.ArrayList;
import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;

public class Vendedor {
 //Propiedades y constructores
 public Integer IdVendedor;
 public String Nombres;
 
 public Vendedor(Integer idvendedor, String nombres)
 {
  this.IdVendedor=idvendedor;
  this.Nombres=nombres;
 }
 
 public Vendedor(){}
 
 /*Metodos*/
 //El spinner muestra lo que retorne este método
 public String toString(){
  return Nombres;
 }
 //getId captura el id del item seleccionado en el spinner
 public int getId()
 {
  return IdVendedor;
 }
 
 //Devuelve lista que se usa para llenar el spinner vendedor
 public ArrayList ConsultarVendedores()
 {
  ArrayList lista = new ArrayList();
  
  //Estos datos los proporciona el webService
  final String NAMESPACE = "http://tempuri.org/";
        final String URL="http://10.0.2.2/Visitas/WebServiceVisitas.asmx"; //para pruebas en el AVD
  //final String URL="http://192.168.1.11/Visitas/WebServiceVisitas.asmx"; //para llevar la apk a produccion
        final String METHOD_NAME = "ConsultarVendedores";
        final String SOAP_ACTION = "http://tempuri.org/ConsultarVendedores";

        //Se instancia un objeto soap pasandole el namespace y el metodo del webservice a consumir
        SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);

        //Se instancia un objeto envelope y se define que version de soap se usará (10,11,12)
        SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
        envelope.dotNet = true; //indica si el web service fue desarrollado en .NET

        envelope.setOutputSoapObject(request);

        //establece la comunicacion por http
        //recordar habilitar permisos de internet
        HttpTransportSE transporte = new HttpTransportSE(URL); 

        try 
        {
                transporte.call(SOAP_ACTION, envelope); //ejecuta la llamada

                SoapObject resSoap =(SoapObject)envelope.getResponse(); //obtiene respuesta
                
                //lo siguiente es para recorrer, leer y guardar la respuesta en una lista
                int nPropiedades = resSoap.getPropertyCount();
                for (int i = 0; i < nPropiedades; i++) 
                {
                   SoapObject ic = (SoapObject)resSoap.getProperty(i);
                   
                   lista.add(new Vendedor(
                     Integer.parseInt(ic.getProperty("IdVendedor").toString()),
                     ic.getProperty("Nombres").toString()));
                }
        } 
        catch (Exception e) 
        {
         e.printStackTrace();
        } 
  return lista;
 }
}



Destacar lo siguiente:
final String NAMESPACE = "http://tempuri.org/";
final String URL="http://10.0.2.2/Visitas/WebServiceVisitas.asmx";
//final String URL="http://192.168.1.11/Visitas/WebServiceVisitas.asmx";
final String METHOD_NAME = "ConsultarVendedores";
final String SOAP_ACTION = "http://tempuri.org/ConsultarVendedores";

Los datos de esas variables las proporciona el webservice, se puede observar de donde tomarlos en la siguiente imagen:
obtener definiciones de soap


Nota 1: Para probar con el Android Virtual Device, se debe setear la URL apuntando a la ip 10.0.2.2 ya que no reconoce con localhost ni con 127.0.0.1 como se suele hacer. Cuando se pruebe la apk en un mobil android, se debe poner la dirección ip del servidor que contiene el webservice.

Nota 2: Si el método del servicio requiere pasarle una propiedad como parámentro, se debe agregar una propiedad al objeto soap de nombre "request":


request.addProperty("IdVendedor", IdVendedor.toString());

Soap para grabar
4. Crear una clase "Visita.java" e implementar la interfaz KvmSerializable, se implementan los métodos:
  • getproperty()
  • getPropertyCount()
  • getPropertyInfo()
  • setProperty()
De la siguiente manera según las propiedades de la clase que se van a enviar a grabar a través del webservice:
 @Override
 public Object getProperty(int arg0) {
  switch(arg0)
        {
        case 0:
            return this.IdVisita;
        case 1:
            return this.IdVendedor;
        case 2:
            return this.IdRuta;
        case 3:
            return this.IdCliente;
        case 4:
            return this.IdTipoVisita;
        case 5:
            return this.Resultado;
        case 6:
            return this.Comentario;
        }
    return null;
 }

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

 @Override
 public void getPropertyInfo(int indice, Hashtable arg1, PropertyInfo info) {
  switch(indice)
        {
        case 0:
            info.type = PropertyInfo.INTEGER_CLASS;
            info.name = "IdVisita";
            break;
        case 1:
            info.type = PropertyInfo.INTEGER_CLASS;
            info.name = "IdVendedor";
            break;
        case 2:
            info.type = PropertyInfo.INTEGER_CLASS;
            info.name = "IdRuta";
            break;
        case 3:
            info.type = PropertyInfo.INTEGER_CLASS;
            info.name = "IdCliente";
            break;
        case 4:
            info.type = PropertyInfo.INTEGER_CLASS;
            info.name = "IdTipoVisita";
            break;
        case 5:
            info.type = PropertyInfo.STRING_CLASS;
            info.name = "Resultado";
            break;
        case 6:
            info.type = PropertyInfo.STRING_CLASS;
            info.name = "Comentario";
            break;
        default:break;
        }
  
 }

 @Override
 public void setProperty(int indice, Object val) {
   switch(indice)
         {
         case 0:
          IdVisita = Integer.parseInt(val.toString());
             break;
         case 1:
          IdVendedor = Integer.parseInt(val.toString());
             break;
         case 2:
          IdRuta = Integer.parseInt(val.toString());
             break;
         case 3:
          IdCliente = Integer.parseInt(val.toString());
             break;
         case 4:
          IdTipoVisita = Integer.parseInt(val.toString());
             break;
         case 5:
          Resultado = val.toString();
             break;
         case 6:
          Comentario = val.toString();
             break;
         default:
             break;
         }
 }

5. Definir el método grabar de la siguiente manera:

public boolean Grabar(boolean esNuevo)
{ 
 boolean resul = true;
 final String NAMESPACE = "http://tempuri.org/";
 final String URL="http://10.0.2.2/Visitas/WebServiceVisitas.asmx";
 //final String URL="http://192.168.1.11/Visitas/WebServiceVisitas.asmx";
 final String METHOD_NAME = "GrabarVisita";
 final String SOAP_ACTION = "http://tempuri.org/GrabarVisita";

 SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
 
 //para cuando se va a pasar un objeto no primitivo
 PropertyInfo pi = new PropertyInfo();
 pi.setName("Visita"); //nombre del objeto a pasar
 pi.setValue(this); //pasar los valores
 pi.setType(this.getClass());//pasar el tipo del objeto

 request.addProperty("EsNuevo", esNuevo); //agregar propiedad 1
 request.addProperty(pi);  //agrega propiedad 2 de tipo Visita

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

 envelope.setOutputSoapObject(request);
 
 //mapea o hace matching entre el objeto Visita de java y 
 //el objeto VisitaInfo de Visual Basic.Net
 envelope.addMapping(NAMESPACE, "VisitaInfo", this.getClass());

 HttpTransportSE transporte = new HttpTransportSE(URL);

 try 
 {
   transporte.call(SOAP_ACTION, envelope);

   SoapPrimitive resultado_xml =(SoapPrimitive)envelope.getResponse();
   String res = resultado_xml.toString();
   
   //verifica si se grabó correctamente
   if(!res.equals("true"))
     resul = false;
 } 
 catch (Exception e) 
 {
   resul = false;
 }  

 return resul;
}

Nota 1: lo nuevo en el método grabar es que al request se le agrega un una propiedad de tipo Visita, y luego hay que hacer un matching entre nuestro objeto java Visita y el objeto VB.Net VisitaInfo. 

Nota 2: El matching no puede realizarse si la clase no tiene implementada la interfaz Kmserializable y las propiedades de los objetos deben encajar en ambos lenguajes así como sus tipos independientemente.


6. Consideraciones en MainActivity.java:
Hay que crear cierta programación para que todo funcione bien, ya que da error porque no se puede ejecutar operaciones de larga duración en el hilo principal (transacciones con webservice) por políticas estrictas, para solucionar esto, hay que cambiar la política:

//Necesario para que no salga error en operaciones de larga duracion en el hilo principal
if (android.os.Build.VERSION.SDK_INT > 9) {
 StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
 StrictMode.setThreadPolicy(policy);
}

Ese código se debe escribir dentro del método onCreate. Caso contrario hay que orientar el código a operaciones en hilos de ejecución (Threads) en un posterior tutorial se dará una explicación.

A este punto ya se ha explicado el funcionamiento de soap y como usarlo. los demás métodos del resto de clases son similares, se deja a mano de la persona que sigue este tutorial la tarea de terminar la aplicación, de igual manera el código fuente completo se pondrá en un enlace al final.

7. Corrrer la aplicación, debe quedar algo como esto:
grabar un nuevo registro
Pantalla Principal
consultar registros
Pantalla de consulta














Descargar Fuentes de los proyectos .Net, Android java y base de datos: a través de este link

4 comentarios:

  1. Estimado estaba mirando tu ejemplo y no puedo levantar la base de datos por me marca que tiene errores el bak.

    ResponderEliminar
    Respuestas
    1. Hola, puede ser la versión del SSMS, acabo de actualizar el skydrive y ahora también incluyo un archivo de script para que lo ejecutes, te crea la base de datos, estructuras, y registros.

      Eliminar
  2. Hola puedees pasarme el link ? ya no esta disponible el que esta publicado =/

    ResponderEliminar
    Respuestas
    1. Claro, aquí está: https://1drv.ms/u/s!AlmTdpYG-a-ahHPGFbY2tVuXGpO2
      gracias por el aviso, voy a actualizar el link.

      Eliminar