O SDK do Android não oferece uma solução fácil e embutida para consumir web services. Neste artigo, vou demonstrar como consumir web service usando a biblioteca KSOAP2.
Vou usar o Web Service que criei no meu artigo Expondo POCO como Web Services usando Spring Framework para criar uma aplicação de conversão de moedas feitas para dispositivos Android.
Só para lembrar, segue a interface do meu web service, que foi desenvolvido em .NET:
using System;
namespace SpringTutorial
{
public interface ICurrencyService
{
decimal Convert(string from, string to, decimal value);
}
}
Primeiro, vou implementar o serviço de conversão que irá consumir o web service de conversão de moedas desenvolvido anteriormente. O KSOAP2 é uma biblioteca que permite o consumo de web services sem a necessidade de geração de código ou uso de proxies dinâmicos. Lembre-se que a aplicação vai rodar em um dispositivo móvel e proxy dinâmico é um recurso caro computacionalmente.
package br.com.zbra.android.sample;
import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapPrimitive;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;
public class ConvertService {
private static final String SOAP_ACTION = "http://zbra.com.br/springtutorial/Convert";
private static final String METHOD_NAME = "Convert";
private static final String NAMESPACE = "http://zbra.com.br/springtutorial";
private static final String URL = "http://192.168.10.103/SpringTutorialService/CurrencyServiceWS.asmx";
public String Convert(String fromCurrency, String toCurrency, String amount) {
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
request.addProperty("from", fromCurrency);
request.addProperty("to", toCurrency);
request.addProperty("value", amount);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
try {
HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
androidHttpTransport.call(SOAP_ACTION, envelope);
SoapPrimitive result = (SoapPrimitive) envelope.getResponse();
return result.toString();
} catch (Exception e) {
return e.getMessage();
}
}
}
O request é feito usando um SoapObject e cada parâmetro da chamada é uma propriedade do SoapObject. A chamada ao servidor de aplicações é feita usando um HttpTransportSE passando um envelope SOAP com a request gerada. Após a execução do serviço, o envelope irá conter o retorno do serviço. No nosso caso, o resultado é um decimal que é serializado como um SoapPrimitive seguindo o protocolo. Se o retorno fosse um objeto complexo, o retorno seria um SoapObject.
Tendo o serviço implementado, é só chamá-lo no Activity da aplicação Android:
package br.com.zbra.android.sample;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Spinner;
public class Main extends Activity {
private Spinner fromCurrencySpinner;
private Spinner toCurrencySpinner;
private EditText amountEdit;
private EditText resultEdit;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
bindControls();
}
private void bindControls() {
fromCurrencySpinner = (Spinner) findViewById(R.id.from_currency_edit);
toCurrencySpinner = (Spinner) findViewById(R.id.to_currency_edit);
amountEdit = (EditText) findViewById(R.id.amount_edit);
resultEdit = (EditText) findViewById(R.id.result_edit);
}
public void onConvertBtnClick(View v) {
String fromCurrency = (String) fromCurrencySpinner.getSelectedItem();
String toCurrency = (String) toCurrencySpinner.getSelectedItem();
String amount = amountEdit.getText().toString();
ConvertService service = new ConvertService();
String result = service.Convert(fromCurrency, toCurrency, amount);
resultEdit.setText(result);
}
}
No arquivo AndroidManifest.xml, é necessário dar permissão para a aplicação poder acessar a internet.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="br.com.zbra.android.sample" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="8" /> <uses-permission android:name="android.permission.INTERNET"></uses-permission> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".Main" 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> </manifest>
Por restrições de espaço e por não ser o escopo do artigo, vou apenas publicar um screenshot da minha aplicação de exemplo.
Vale observar que o consumo de Web Services em dispositivos móveis não é recomendado pela equipe de desenvolvimento do Android devido ao overhead que o processamento do SOAP exige. Se você tem controle sobre o servidor, o ideal é usar arquiteturas baseadas em REST tal como ODATA.
Referencias:
- KSOAP2 – http://ksoap2.sourceforge.net/
- KSOAP2 for Android – http://code.google.com/p/ksoap2-android/
- Open Data Protocol (OData) – http://www.odata.org/

14 Comentários
Por Luiz em 20 de September de 2011 às 20:58
Olá Ricardo, interessante a biblioteca KSOAP2. Vou utiliza-la em alguns exemplos e curiosidades que descobrir compartilho aqui no ZBRA.
Por Renato em 4 de November de 2011 às 15:56
Estou desenvolvendo uma aplicação e nela eu tenho que buscar um objeto com vários parametro, ex: nome, idade, cpf, endreço. Eu quero apenas mostrar o nome da pessoa, como eu devo fazer? O xml de retorno tem que ter um padrão (header, body) e etc?
Por Ricardo Ushisima em 6 de November de 2011 às 14:35
Renato,
uma requisição de um serviço Web Service e a respectiva resposta (SOAP Messages) sempre possuem um cabeçalho (SOAP Header) e corpo (SOAP Body). Se não, não é uma resposta válida para Web Services. Se o retorno do seu serviço é muito complexo e e esta atrapalhando na transmissão de dados para sua aplicação, desenvolva um novo serviço que retorne apenas informações que você vai utilizar.
No meu exemplo, o retorno do meu serviço é apenas um decimal, mas para o seu caso, muito provavelmente o que você quer é um serviço que retorne uma array de alguma coisa (pelos atributos, parece uma Pessoa).
Outra opção, que é a recomendada pelo SDK do Android como citei no artigo, é usar serviços REST. Neste caso, o retorno do serviço é mais simples, geralmente um XML simples, bem mais flexível que mensagens SOAP. No momento não tenho exemplos de serviços REST para mostrar.
Vale a pena ler os demais artigos sobre Android e Web Services que temos no blog da ZBRA:
http://zbra.com.br/2011/07/06/androidksoap-pt-1-transcrevendo-soapobjects-atraves-de-annotations-reflection/
http://zbra.com.br/2011/02/25/consumindo-web-services-usando-proxy-dinamico/
http://zbra.com.br/2010/12/23/expondo-poco-como-web-services-usando-spring-framework/
Por joab silveira em 21 de February de 2012 às 13:46
Opa ricardo, to com um probleminha
fiz um web service simple com um webmethod que recebe dois parametros
namespace SmartPDVSinc
{
///
/// Summary description for Service
///
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class Service : System.Web.Services.WebService
{
[WebMethod]
public string mensagem(string primeironome, string segundonome)
{
return “nome completo ” + primeironome + ” ” + segundonome;
}
}
}
na minha aplicacao android coloquei da seguinte maneira
public class LocalService {
private static final String SOAP_ACTION = “http://tempuri.org/mensagem”;
private static final String METHOD_NAME = “mensagem”;
private static final String NAMESPACE = “http://10.0.2.2:45120″;
private static final String URL = “http://10.0.2.2:45120/Service.asmx”;
public String mensagem(String primeironome, String segundonome) {
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
//envelope.dotNet = true;
envelope.setOutputSoapObject(request);
request.addProperty(“primeironome”, primeironome);
request.addProperty(“segundonome”, segundonome);
try {
HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
androidHttpTransport.call(SOAP_ACTION, envelope);
SoapPrimitive result = (SoapPrimitive) envelope.getResponse();
return result.toString();
} catch (Exception e) {
return e.getMessage();
}
}
}
no entando os parametros primeironome e segundo nao chegam ao webservice
a mensagem retornada eh sempre “nome completo ” sem os valores passados nos parametros
desde ja agradeco
abraco
Por Ricardo Ushisima em 2 de March de 2012 às 19:38
Joab,
vc verificou se a aplicação android esta realmente passando os parametros para o seu serviço?
Por que vc comentou a linha envelope.dotNet = true; sendo que vc esta acessando um WS feito em .NET?
Vc verificou se mensagem gerada pelo kSOAP tem os valores corretos? (use http://www.fiddler2.com para interceptar o pacote).
Ricardo
Por Rodrigo Dapper em 12 de March de 2012 às 17:44
Ricardo, boa tarde, tudo certo.
eu estou com dificuldades de conseguir enviar um xml da minha aplicação para um webservice, o arquivo é pequeno com poucas informações. mas estou com muitas dificuldades, por favor, será que consegue me ajudar. desde já obrigado.
Por Ricardo Ushisima em 19 de March de 2012 às 12:13
Oi Rodrigo,
preciso de mais informação para tentar ajudar no seu problema.
Por Jefferson em 22 de March de 2012 às 17:25
Saudações…
Consegui consumir um ws .asmx com sucesso. Mas com um ws WCF .svc não virou. Vc sabe se é possivel acessa-lo pelo ksoap2?
Por Ricardo Ushisima em 23 de March de 2012 às 11:46
Sim, é possível consumir serviços WCF desde que o binding WCF seja do tipo BasicHttp. Se o serviço usa DataContracts, o xml gerado no corpo do SOAP Request é um pouco diferente. Se usa Message Contract, o xml deve ser o mesmo. Use o Fiddler (http://www.fiddler2.com/) para inspecionar a troca de xml e montar o SOAP Object no Android de maneira apropriada.
Por felipe Bonatto em 6 de April de 2012 às 20:19
Olá Ricardo tudo bem? Estou com problemas para consumir um webservice em Axis2. Fiz todos os procedimentos mas na hora do androidHttpTransport.call() ele ja pula para o meu catch. Estou vendo as passagens de valores e aparentemente estão corretos. Coloquei a mensagem do e.getMessage() e ele retorna “Cannot serialize: “valor”.
Por Ricardo Ushisima em 9 de April de 2012 às 16:41
O valor é um decimal? Esta no formato decimal correto (separado por ponto)?
Por felipe Bonatto em 9 de April de 2012 às 18:53
Opa Ricardo, então eu finalmente consegui consumir, porém percebi que para tipos double e float ele vai com “10.0″ por exemplo, eu devo tratá-lo antes?
Por Ricardo Ushisima em 12 de April de 2012 às 15:08
Não deveria ser necessário tratar os números antes.
Por Bruno Ap em 15 de April de 2012 às 18:22
Boa tarde Ricardo , seria possivel enviar um array de tipos primitivos para o webservice em request.addProperty()?
Por exemplo
String [] valores ={“a”,”b”};
request.addProperty(“parametro”, valores)