Android+KSoap – Pt.2: Parâmetros Complexos

No artigo anterior, vimos como transcrever os dados retornados por um WebService em objetos do modelo da nossa aplicação. Para isso criamos um algorítimo, fundamentado em Annotations e Reflection, que possibilitava essa transcrição de maneira completamente genérica, evitando escrever aqueles códigos chatos de parsing.

Desta vez, vamos nos ater ao envio desses dados ao servidor, ou seja, como utilizar objetos complexos como parâmetros para nossos métodos remotos? Vamos construir uma solução genérica, também baseada em nas anotações e reflexão, mais especificamente, na estrutura já desenvolvida na Pt.1, e dessa vez sem cair nos mesmos erros da primeira implementação.

O Problema

A biblioteca KSoap2 oferece uma alternativa para passagem de parâmetros complexos, a qual, devido à limitação tecnológica imposta na época do desenvolvimento da lib, não pôde se beneficiar das features avançadas oferecidas pelo Java 5.

A solução oferecida, depende da implementação da interface da KvmSerializable. Essa interface é declarada da seguinte maneira.

public interface KvmSerializable {
    /**
     * Get the property at the given index
     */
    Object getProperty(int index);

    /**
     * @return the number of serializable properties
     */
    int getPropertyCount();

    /**
     * Sets the property with the given index to the given value.
     *
     * @param index
     *            the index to be set
     * @param value
     *            the value of the property
     */
    void setProperty(int index, Object value);

    /**
     * Fills the given property info record.
     *
     * @param index
     *            the index to be queried
     * @param properties
     *            information about the (de)serializer.  Not frequently used.
     * @param info
     *            The return parameter, to be filled with information about the
     *            property with the given index.
     */
    void getPropertyInfo(int index, Hashtable properties, PropertyInfo info);
}

Essa interface praticamente obriga o desenvolvedor a usar switch-cases para definir que campo você vai retornar ou atualizar e é altamente frágil, facilitando a ocorrência de erros, isso sem falar na dificuldade na manutenção. Como todos sabem, switch-case is evil, mas não vamos jogar pedras no pessoal, pois JME impunha severas limitações. Além dos problemas supracitados, é necessária a implementação dessa interface para cada objeto complexo que você quer enviar (além de alguns truques extras que vamos comentar mais tarde), o que deixa a manutenabilidade ainda mais comprometida. Veja um exemplo de implementação dessa interface abaixo.

package br.com.zbra.test.model;

import java.util.Hashtable;

import org.ksoap2.serialization.KvmSerializable;
import org.ksoap2.serialization.PropertyInfo;

public class ClienteHardcoded implements KvmSerializable{

	private long id;
	private String nome;
	private String endereco;

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}

	public String getEndereco() {
		return endereco;
	}

	public void setEndereco(String endereco) {
		this.endereco = endereco;
	}

	public Object getProperty(int index) {
		switch (index) {
		case 1:
			return id;
		case 2:
			return nome;
		case 3:
			return endereco;
		}
		return null;
	}

	public int getPropertyCount() {
		return 3;
	}

	@SuppressWarnings("rawtypes")
	public void getPropertyInfo(int index, Hashtable properties, PropertyInfo pi) {
		String name = null;
		Class<?> type = null;

		switch (index) {
		case 1:
			type = long.class;
			name = "id";
		case 2:
			type = String.class;
			name = "nome";
		case 3:
			type = String.class;
			name = "endereco";
		}

		pi.type = type;
		pi.name = name;
	}

	public void setProperty(int arg0, Object arg1) {
		// ignore
	}
}

A Solução

A solução proposta é bem simples e, novamente, baseada em Annotations e Reflection. Não vou repetir o código do artigo anterior aqui, logo se você chegou no meio da festa, melhor dar uma olhadela na parte 1. Lá definimos os Annotations que vamos utilizar aqui, assim como o cache de anotações que implementamos para solucionar o problema de performance. A proposta consiste de criar uma classe que empacote nossos modelos, e faça toda a implementação dos métodos da interface KvmSerializable de forma centralizada e completamente desacoplada dos nossos modelos. Ao utilizar a nova classe, seria apenas necessário empacotar a instância do nosso modelo nessa classe e tudo vai funcionar como que por mágica. Vamos dar uma olhada no código.

package br.com.zbra.test.soap.serializable;

import java.util.Hashtable;
import java.util.List;

import org.ksoap2.serialization.KvmSerializable;
import org.ksoap2.serialization.PropertyInfo;

import br.com.zbra.test.soap.util.SOAPAnnotationDataCache;
import br.com.zbra.test.soap.util.SOAPAnnotationDataCache.AnnotatedClassData;
import br.com.zbra.test.soap.util.SOAPAnnotationDataCache.AnnotatedFieldData;

public class Wrapper implements KvmSerializable {

	private Object object;
	private AnnotatedClassData classData;
	private List<AnnotatedFieldData> fieldsData;

	protected Wrapper() {
		this.object = this;
		initialize();
	}

	public Wrapper(Object object) {
		this.object = object;
		initialize();
	}

	private void initialize() {
		classData = SOAPAnnotationDataCache.getAnnotatedClassData(object.getClass());
		fieldsData = classData.getFields();
	}

	public Object getProperty(int index) {
		Object result = null;

		try {
			result = fieldsData.get(index).getField().get(object);
		} catch (Throwable e) {
			e.printStackTrace();
		}

		return result;
	}

	public int getPropertyCount() {
		return fieldsData.size();
	}

	@SuppressWarnings("rawtypes")
	public void getPropertyInfo(int index, Hashtable properties, PropertyInfo info) {
		AnnotatedFieldData fieldData = fieldsData.get(index);

		info.type = fieldData.getField().getType();
		info.name = fieldData.getName();
	}

	public void setProperty(int index, Object value) {
		// not necessary
	}
}

Utilizando a classe Wrapper, em conjunto com os metadados fornecidos pelas anotações, transformar qualquer classe em um KvmSerializeble é questão de uma única linha de código.

Cliente cliente = new Cliente();
// inicialização dos atributos do cliente...
KvmSerializeble serializableCliente = new Wrapper(cliente);

Voilà! Eis que temos nossa implementação da interface KvmSerializable completamente modularizada e pronta para ser usada com classes as quais ela nem sequer sabe da existência. Maravilha, não? Bom! Mas isso não é tudo. Ainda precisamos fazer alguns truques para que essa classe seja transmitida com sucesso. Segue o código necessário para invocar um serviço passando essa classe como parâmetro.

package br.com.zbra.test;

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

import br.com.zbra.test.model.Cliente;

public class SaveClientService {
    private static final String SOAP_ACTION = "http://zbra.com.br/springtutorial/SaveCliente";
    private static final String METHOD_NAME = "SaveCliente";
    private static final String NAMESPACE = "http://zbra.com.br/springtutorial";
    private static final String NAMESPACE_CLIENTE = "http://zbra.com.br/springtutorial";
    private static final String URL = "http://192.168.10.103/SpringTutorialService/SaveClienteWS.asmx";

    public void saveClient(Cliente cliente) {
    	try {
            SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);

            PropertyInfo pi = new PropertyInfo();
            pi.name = "cliente";
            pi.setValue(new Wrapper(cliente));
            pi.setType(Wrapper.class);
            pi.setNamespace(NAMESPACE_CLIENTE);

            request.addProperty(pi);

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

            envelope.addMapping(NAMESPACE_CLIENTE, "Cliente", Wrapper.class);

            HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
            androidHttpTransport.call(SOAP_ACTION, envelope);

        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

As diferenças entre esse método e o exemplo apresentado pelo Ricardo Ushisima no seu artigo estão concentradas na criação do objeto PropertyInfo (linhas 22-26) e na linha 34.

No exemplo apresentado pelo Ricardo, apenas valores primitivos (Strings) eram passados como parâmetro. Em casos como esse, o método o método SoapObject.addProperty(String, String) é muito útil pois esconde a criação dos PropertyInfos e deixa o código bem legível. Por outro lado, passar objetos complexos como parâmetros exige que você crie o PropertyInfo manualmente, especificamente em função da linha 26. Sem ela, o servidor não receberá o parâmetro da forma correta. Esse parâmetro deve indicar o namespace pelo qual o WS identifica esse tipo, assim ele poderá instanciar o objeto correto quando receber o request. Note também que na linha 24, o valor do PropertyInfo é na verdade uma instância de Wrapper, bem como na linha 25 o tipo é também o Wrapper e não o Cliente.

Já a linha 34, informa que os parâmetros do tipo indicado (no caso a classe Wrapper), devem ser identificados pelo namespace e nome informados. Note que a identificaçáo “Cliente” se refere ao nome usado pelo servidor para referenciar esse tipo e não o nome da sua classe.

Contudo, essa solução não vai funcionar caso você precise encapsular múltiplos parâmetros de tipos diferentes no Wrapper. Isso vai fazer com que o KSoap se perca e gere erros, afinal você está associando todos esses parâmetros de tipos diferentes ao mesmo serializador e essa não era uma situação esperada pela biblioteca.

Neste caso a solução mais simples é fazer com que sua classe estenda Wrapper, e passar o próprio Cliente como parâmetro no mapeamento, ao invés do Wrapper em si. O código do Wrapper vai continuar funcionando da mesma maneira, pois quando invocado o seu construtor padrão, ele considera que a classe a ser serializada é ela mesma, ou seja, esse cenário está previsto desde o inicio. Vejamos como fica o código de envio no caso do Cliente herdar de Wrapper ao invés de apenas ser empacotado nele.

package br.com.zbra.test;

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

import br.com.zbra.test.model.Cliente;

public class SaveClientService {
    private static final String SOAP_ACTION = "http://zbra.com.br/springtutorial/SaveCliente";
    private static final String METHOD_NAME = "SaveCliente";
    private static final String NAMESPACE = "http://zbra.com.br/springtutorial";
    private static final String NAMESPACE_CLIENTE = "http://zbra.com.br/springtutorial";
    private static final String URL = "http://192.168.10.103/SpringTutorialService/SaveClienteWS.asmx";

    public void saveClient(Cliente cliente) {
    	try {
            SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);

            PropertyInfo pi = new PropertyInfo();
            pi.name = "cliente";
            pi.setValue(cliente);
            pi.setType(Cliente.class);
            pi.setNamespace(NAMESPACE_CLIENTE);

            request.addProperty(pi);

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

            envelope.addMapping(NAMESPACE_CLIENTE, "Cliente", Cliente.class);

            HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
            androidHttpTransport.call(SOAP_ACTION, envelope);

        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

Como solução alternativa você pode consertar o KSoap para que ele entenda o cenário supracitado, afinal o código deles é aberto. Fazer isso engessa um pouco o sistema, pois torna cada vez mais complexo manter a biblioteca atualizada, e é possível que você se veja em posição de querer adotar uma versão mais nova em função de alguma funcionalidade recentemente incluída e notar que isso vai ter um custo e impacto muito maior do que deveria. Isso sem falar nas dezenas de bugfixes que você provavelmente nunca vai ter tempo de portar. Olhando por essa perspectiva, gerar o acoplamento de uma herança nem é tão mau assim, certo?

Conclusão

Nesse artigo vimos como transmitir nossos tipos complexos para o servidor como parâmetros para nossos métodos remotos. Utilizamos a infra-estrutura de anotações criada na parte 1 e implementamos uma maneira genérica para plugar nossas classes ao modelo de serialização oferecido pelo KSoap2 e apontamos alguns problemas que você pode encontrar no caminho. Agora estamos todos prontos para colocar a mão na massa, certo? Errado.

Na terceira – e última – parte dessa série, vamos estudar como evitar a duplicação de código das chamadas aos serviços. Abstraindo também essa parte do sub-sistema de invocação de métodos remotos, vamos conseguir como resultado uma solução muito elegante e encapsulada. Isso também vai ajudar a desacoplar nosso sistema do KSoap, tornando-o assim mais robusto e manutenível. Até lá!

29 Comentários

  • Reply

    Por Cesar Pizolato em 16 de September de 2011 às 11:33

    Amigo, vi seu post e me ajudou muito. Esotu desenvolvendo uma aplicação WEB (Java) com integração com Android.
    Tenho um WS feito em java e ele espera um objeto como parametro. (Objeto usuario que criei em java).
    Com seu exemplo consegui consumir com um objeto simples numa boa.
    Usuario u = new Usuario(); // Passo o u e funciona OK.

    Como eu faria para consumir usando um Array de usuario? Usuario[] ? Fazendo isso ele dá erro, falando que a classe Usuario[] não tem a anotação @SoapObject, mas minha classe Usuario tem.

    Sugestões ?

    Obrigado.

    • Reply

      Por Bruno Vinicius em 16 de September de 2011 às 15:22

      Olá Cesar. No exemplo que eu apresentei, eu não contemplei a conversão/envio de coleções/arrays.
      Para recebê-las do serviço, basta você fazer um for varrendo os SoapObjects e chamando o parseAnnotatedVO para cada um. Por exemplo:

      ArrayList list = new ArrayList();
      for (int i = 0; i < response.getPropertyCount(); i++) {
      SoapObject usuarioSoap = (SoapObject)response.getProperty(i);
      Usuario usuario = SoapUtils.parseSoapObjectIntoAnnotatedVO(Usuario.class, usuarioSoap);
      list.add(usuario);
      }
      return list;

      Boa sorte! Se obtiver progressos, fique a vontade para compartilhar aqui.

    • Reply

      Por Fabio Falavinha em 16 de September de 2011 às 17:50

      Olá Cesar, complementando a solução do Bruno, você pode consultar no link abaixo um exemplo simples para envio de tipos complexos usando kSOAP através do tipo DataSoapObject. (http://people.unica.it/bart/2010/12/03/ksoap2-patch-for-user-defined-objects-and-arrays/) => Boa Leitura! :P

  • Reply

    Por Fabio Falavinha em 16 de September de 2011 às 18:00

    Outro link interessante é um patch feito para a versão 2.5.5, que ajuda no envio/parse de tipos complexos (http://code.google.com/p/ksoap2-android/wiki/CodingTipsAndTricks?ts=1306514452&updated=CodingTipsAndTricks). :P

  • Reply

    Por Aldivone Correia em 1 de October de 2011 às 10:24

    Bruno,

    Tu terias algum exemplo passando uma lista de clientes por exemplo para o webservice em java.
    Estou usando o jboss no lado servidor e as anotações @WebService,

    Grato

    • Reply

      Por Bruno Vinicius em 3 de October de 2011 às 13:12

      Infelizmente não, Aldivone. Esse código só foi testado com serviços .net, mas em tese, a única coisa que você precisaria mudar seria a propriedade envelope.dotNet = false. O resto deveria funcionar normalmente.

  • Reply

    Por Roni em 16 de October de 2011 às 13:47

    Olá. Muito interessante o tutorial. Estou com um problema no response do KSOAP. Tenho um array de retorno, mas se ele passa de um número de itens não retorna e dá uma exception “Read Timed Out”. Será que o KSOAP tem um limite de tamanho no request/response? Ou é um limite imposto pelo servidor, no meu caso, WS com EJB´s Stateless do JBoss.
    Obrigado.

    • Reply

      Por Bruno Vinicius em 17 de October de 2011 às 10:14

      Não acho que o KSoap imponha tal limitação. Se o você está retornando dados maiores, como imagens, vale separar em duas chamadas ao serviço. Uma que retorna os dados sem as imagens e uma que retorna a imagem, dado um determinado id. Dessa maneira você pode carregar as imagens sob demanda. Uma outra possibilidade é implementar paginação. Aumentar o tempo de timeout do server é uma decisão perigosa e provavelmente não vai realmente resolver o problema. Mais tarde, um request especifico pode continuar gerando o erro. Espero ter ajudado. Boa sorte!

  • Reply

    Por Roni em 18 de October de 2011 às 14:12

    O que está ocorrendo é muito estranho. Tentei aumentar o timeout em várias configurações no cliente e no servidor, mas não deu. Raramente dá certo, e os dados vem. Peguei o fonte do KSOAP e estou depurando há dias, não tem uma condição específica pra ocorrer esse erro. O trecho do stackTrace é esse:
    java.net.SocketTimeoutException: Read timed out
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(SocketInputStream.java:150)
    at java.net.SocketInputStream.read(SocketInputStream.java:121)
    at java.io.BufferedInputStream.read1(BufferedInputStream.java:273)
    at java.io.BufferedInputStream.read(BufferedInputStream.java:334)
    at java.io.FilterInputStream.read(FilterInputStream.java:133)
    at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(HttpURLConnection.java:2968)
    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283)
    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325)
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
    at java.io.InputStreamReader.read(InputStreamReader.java:184)
    at org.kxml2.io.KXmlParser.peek(KXmlParser.java:582)
    at org.kxml2.io.KXmlParser.readName(KXmlParser.java:619)
    at org.kxml2.io.KXmlParser.parseEndTag(KXmlParser.java:331)
    at org.kxml2.io.KXmlParser.nextImpl(KXmlParser.java:169)
    at org.kxml2.io.KXmlParser.next(KXmlParser.java:1040)
    at org.ksoap2.serialization.SoapSerializationEnvelope.readUnknown(SoapSerializationEnvelope.java:133)
    at org.ksoap2.serialization.SoapSerializationEnvelope.read(SoapSerializationEnvelope.java:279)
    at org.ksoap2.serialization.SoapSerializationEnvelope.parseBody(SoapSerializationEnvelope.java:59)
    at org.ksoap2.SoapEnvelope.parse(SoapEnvelope.java:69)
    at org.ksoap2.transport.Transport.parseResponse(Transport.java:61)
    at org.ksoap2.transport.HttpTransportSE.call(HttpTransportSE.java:112)
    at org.ksoap2.transport.HttpTransportSE.call(HttpTransportSE.java:44)

    Se alguém puder me dar pistas do motivo do Read Timed Out, agradeço muito.

    Valeu

    • Reply

      Por Bruno Vinicius em 20 de October de 2011 às 13:09

      Oi Roni. Você viu o minha resposta ao seu comentário anterior? Você pode dar mais informações sobre seu cenário? O que você está tentando buscar, por exemplo.
      Esse erro não é levantado pelo KSoap exatamente, mas sim pela API de Sockets do Java. A causa mais comum para a ocorrência de erros desse gênero é que os dados que você está tentando buscar são muitos ou muito grandes para serem recuperados em uma única chamada. Um outro possível problema são firewalls. se você estiver fazendo chamadas a um serviço em outra máquina o firewall do windows pode estar bloqueando a conexão externa para sua aplicação, mas isso é menos provável uma vez que você diz que eventualmente seus requests funcionam…

  • Reply

    Por Roni em 24 de October de 2011 às 4:43

    Será que a quantidade de dados retornados é uma limitação do KSoap ou da própria especificação de WS? Se há limite, tu saberia me dizer de quantos bytes seria?

    Obrigado

    • Reply

      Por Bruno Vinicius em 24 de October de 2011 às 13:29

      Eu desconheço qualquer limitação de qualquer uma das tecnologias.

  • Reply

    Por Fabio Falavinha em 24 de October de 2011 às 13:32

    Olá Roni, o que você está retornando via WebService? Você pode dizer a quantidade de dados que deveriam retornar?

    Como o Bruno disse, a especificação WS ou o framework kSOAP não implementam limitação no acesso/retorno de dados.

    Fiz uma pesquisa no site do kSOAP, referente ao problema de Time Out que está acontecendo em seu código, achando os seguintes links:

    http://code.google.com/p/ksoap2-android/issues/detail?id=52

    http://code.google.com/p/ksoap2-android/issues/detail?id=60&q=httpTransportse%20timeout

    Veja se os links podem ajudá-lo a depurar melhor o código. Mas, fico no aguardo pelas respostas das duas perguntas acima, no comentário.

    []‘s

  • Reply

    Por Roni em 30 de October de 2011 às 16:58

    Olá. Consegui resolver o problema tirando o vector de dentro do objeto. Anteriormente o objeto estava assim:

    public class TipoUsuario implements KvmSerializable {
    private Integer id;
    private String descricao;
    private String tipoRegistro;
    private List permissaoList;

    // implementacoes dos métodos de KvmSerializable

    // outra classe
    public class Permissao implements KvmSerializable {
    private Integer id;
    private Modulo modulo;
    private Boolean canRetrieve;
    private Boolean canCreate;
    private Boolean canUpdate;
    private Boolean canDelete;
    private Boolean canPrint;

    // outra classe
    public class Modulo implements KvmSerializable {
    private Integer id;
    private String nome;
    private String env;

    O que vinha era 5 tipos de usuário, com 12 permissões cada um.

    Realmente acredito que tais tecnologias não possuam limitação de quantidade de dados trafegadas. Talvez algum objeto dentro desse processo todo fique sobrecarregado. Mas por hora, consegui dar um “jeitinho”.
    Agradeço a ajuda de vocês.

  • Reply

    Por Douglas Cavalheiro em 22 de January de 2012 às 10:55

    Olá Bruno, parabéns pelo artigo, muito científico e prático.
    Só estou tendo problemas em relação a talvez webservices em Jax-WS

    a chamada do método é esta:
    @WebMethod
    public boolean gravaVenda(@WebParam(name=”venda”) Venda venda){
    if(venda != null){
    return true;
    }else{
    return false;
    }
    }

    Já a comunicação está semelhante a sua:

    PropertyInfo pi = new PropertyInfo();
    pi.setValue(new Wrapper(venda));
    pi.setType(Wrapper.class);
    pi.setName(“venda”);
    pi.setNamespace(Constants.SOAP_NAMESPACE);

    SoapObject request = new SoapObject(getNamespace(), getMethodName()); //create the soap method

    request.addProperty(getPropertyInfo());

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

    if(classMapping != null){
    envelope.addMapping(namespace, getPropertyInfo().getName(), classMapping);
    }

    MarshalDouble md = new MarshalDouble();
    md.register(envelope);

    MarshalDate mdate = new MarshalDate();
    mdate.register(envelope);

    HttpTransportSE androidHttpTransport = new HttpTransportSE(getUri());
    androidHttpTransport.call(getSoapAction(), envelope);

    Não consigo enxergar erros em relação ao seu tutorial, tive que adicionar os Marshals para converter double e date, mas acredito não ser isso.

    Se puder me ajudar, agradeço

    • Reply

      Por Douglas Cavalheiro em 22 de January de 2012 às 11:00

      Ah esqueci de comentar, o objeto chega ao webservice como nulo, a comunicação funciona, porém ele não vem instanciado.

    • Reply

      Por Bruno Vinicius em 24 de January de 2012 às 11:02

      Opa Douglas, tudo bem?

      Primeiramente, obrigado pelos elogios. Eu tentei, por pouco tempo devo adimitir, fazer esse código funcionar com um serviço Jax-WS, mas sem sucesso. Imagino que a classe Venda já esteja implementando a interface KvmSerializable, se não pode ser esse o problema. Se sim, sugiro que ligue a opção Debug no envelope (androidHttpTransport.debug = true; depois procure as propriedades bodyIn e bodyOut ) . Com essa opção ligada é possível inspecionar o XML gerado pelo KSoap para procurar eventuais problemas. Se possível, recomendo que teste esse mesmo WS usando um cliente padrão e compare os dois requests para observar alguma diferença entre ambos.

      Qualquer novidade, da uma passada aqui pra nos atualizar.
      []s

      • Reply

        Por Douglas Cavalheiro em 25 de January de 2012 às 22:14

        Bruno, obrigado pela atenção, acabei descobrindo meio sem querer como funcionar com JaxWs:

        Ao invés de utilizar o PropertyInfo como abaixo:
        PropertyInfo pi = new PropertyInfo();
        pi.setValue(new Wrapper(venda));
        pi.setType(Wrapper.class);
        pi.setName(“venda”);
        pi.setNamespace(Constants.SOAP_NAMESPACE);

        adicionei diretamente como propriedade o objeto assim:

        KvmSerializable serializableVenda = new Wrapper(venda);
        request.addProperty(“venda”, serializableVenda);

        Desta forma ele entendeu o parametro, não sei se assim o WS em .Net funcionaria também, até é mais prático, estou utilizando suas classes para montar um framework em conjunto com o Ksoap2 assim como o droidpersistence (http://code.google.com/p/droidpersistence) que fiz para mapear classes via annotation e criar banco e tudo mais…

        Abraço e obrigado pela ajuda

        • Reply

          Por Bruno Vinicius em 26 de January de 2012 às 10:21

          Legal Douglas!

          Eu comecei a organizar uma biblioteca num projeto no google code também, mas deixei um pouco a algum tempo.
          Se você quiser posso te incluir como commiter e a gente pode evoluir ela. Está aqui. http://code.google.com/p/asoap/
          Ela está com basicamente tudo que está nos 4 artigos implementado mais algumas coisinhas ainda. Da uma olhada lá.

          []s

          • Por Douglas Cavalheiro em 26 de January de 2012 às 12:36

            Vi seu projeto, achei bem legal Bruno, me envie um e-mail para falarmos mais sobre isso e eu passar meu email google.

            Até mais!

  • Reply

    Por Aluizio em 13 de September de 2012 às 14:11

    Olá..estou ressuscitando a discussão. Como faria no caso do envio de um bitmap? estou enviando um byte[] porém esta dando erro de sockettimeoutexception. Ás vezes o envio segue normal, ás vezes demora muito e dá essa excessão.

    • Reply

      Por Bruno Vinicius em 19 de September de 2012 às 11:34

      Olá Aluizio!

      O seu problema provavelmente acontece em função do tamanho do arquivo enviado e velocidade de envio (largura da banda).
      Uma solução simples, seria aumentar o tempo de timout do servidor. Se isso não é uma opção você pode fazer um algoritimo para tentar
      enviar novamente no caso de timeout, ou esperar até ter uma conexão de dados mais confiável (WiFi) para fazer a transferencia.

  • Reply

    Por Andre em 11 de November de 2013 às 16:44

    Olá, sei que o tópico é antigo, porém, se alguém tiver alguma ideia do que possa estar acontecendo :

    http://zbra.com.br/2011/08/19/androidksoap-pt-2-parametros-complexos/

    Obs.: Segui o tutorial todo, não tive problemas com recebimento ou envio de objetos complexos, o problema é quando envio listas de objetos aninhados…. meu web service recebe elas com valores zerado…(como descrito no link acima)

    Att.

    • Reply

      Por Andre em 11 de November de 2013 às 16:45

      Perdão, fiz ctrl+c ctrl+v errado…
      o link do problema é este:

      http://www.guj.com.br/java/305627-enviar-objetos-aninhados-ksoap2

    • Reply

      Por Bruno Vinicius em 13 de November de 2013 às 16:34

      Olha André realmente tem bastante tempo que não mexo nisso, mas eu tenho a impressão que tudo está relacionado a essa linha aqui:

      envelope.addMapping(NAMESPACE_CLIENTE, “Cliente”, Wrapper.class);

      Vc tem que usar esse carinha pra “ensinar” ao parser como produzir o SOAP para essas classes aninhadas quando elas aparecerem.

      Eu posso estar enganado mas acho que o caminho é esse… No meu ultimo artigo eu fiz uma library que cobria bastantes cenários. A versão mais atualizada está aqui https://github.com/brunovinicius/ASOAP/

      Tenho impressão que ela já funciona nesse caso que vc falou…

  • Reply

    Por Julio em 7 de February de 2014 às 8:18

    Caro Bruno Vinícius…
    Muito interessante o seu artigo e com certeza ele me será muito útil. Estou enfrentando um problema na utilização do KSoap2 em um serviço em que eu realizo o download de um arquivo e no response deste método eu retorno o array de bytes e uma string com o hash MD5 deste arquivo para validação. como descrito no wsdl abaixo:

    A minha classe de definição do objeto de response está OK, conforme o exemplo que vc mostrou e não lança nenhuma exceção, só que não faz o download do arquivo. o objeto DownloadArquivoResult vêm com valor nulo e a string strChaveMD5 vêm vazia. Já pesquisei muito e não vi a oridem do problema, em sua experiência vc sabe o que pode ser a origem do erro?

    • Reply

      Por Bruno Vinicius em 7 de February de 2014 às 14:37

      Olá Júlio! Eu lembro que tive problemas transmitindo byte arrays pq o KSoap funciona de uma maneira peculiar com esse tipo de dado, mas infelizmente faz muito tempo e não consigo lembrar exatamente qual o problema. Dá uma olhadela nesse projeto que eu joguei no GitHub. http://code.google.com/p/asoap/ Acho que lá o tratamento para byte arrays está feito.

  • Reply

    Por Datebayo em 9 de August de 2014 às 14:21

    Grande implementação Bruno Vinicius os meus parabéns cara!Nota-se que tiveste algum trabalho nisso.
    É o seguinte, eu estou a tentar obter um objecto do webservice (SoapService em java)
    da-me java.lang.ArrayIndexOutOfBoundsException: 1
    Isto aplica-se ao enviar também!
    E segui a risca a tua implementação utilizando anotaçoes e reflexão.
    Alguma dica?
    Aqui vai o link com o meu codigo:
    https://dl.dropboxusercontent.com/u/5920511/DroidApp.zip

  • Reply

    Por Datebayo em 9 de August de 2014 às 18:56

    Boas afinal ja funciona!
    Como faço para retornar um ArrayList (ou array se facilitar) de tipo composto?





Desenvolvido por hacklab/ com WordPress