Ajuda em exemplo de WebService em Android [RESOLVIDO]

Olá pessoal!

Criei um webservice em Java, usando o seguinte tutorial:

PRIMEIRO EXEMPLO: http://imasters.com.br/artigo/1863/java/web_services_in_java/

Consigo consumi-lo em Java, usando a função somar, como mostrado no link acima. Mas não estou conseguindo consumir em Android. Dei uma pesquisada no Google, e achei o seguinte link:

SEGUNDO EXEMPLO: http://zbra.com.br/2011/03/30/consumindo-web-service-em-aplicacoes-android/

Porém, não consegui entender como eu adaptaria o primeiro exemplo para o segundo.

Para enriquecer o que estou expondo, mostro abaixo o trecho de código do primeiro exemplo que consome o Webservice:

import org.apache.axis.client.Call;
import org.apache.axis.client.Service;

public class UseWS {
    public static void main(String[] args) {
        try {
            String urlWS = "http://localhost:8080/axis/Calculator.jws";
            Object[] params = {new Integer(1), new Integer(1)};

            Service service = new Service();
            Call call = (Call) service.createCall();
            call.setTargetEndpointAddress(urlWS);
            call.setOperationName("somar");
            Integer ret = (Integer) call.invoke(params);
            System.out.println("Resultado: " + ret);
        }
        catch(Exception ex) {
            ex.printStackTrace();
        }
    }
}

E agora, o trecho do segundo exemplo que consome o Webservice (neste caso, um Webservice com URL diferente do primeiro exemplo):

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();
        }
    }
}

Minha dúvida é:

Como seto as constantes abaixo, adaptando o Webservice do primeiro exemplo para a plataforma Android? Eu queria usar a função somar do primeiro exemplo. Mas como seria seu endereço de namespace? E sua soap_action? E como eu passaria parâmetros para o Webservice que será consumido em Android?

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";

Atualmente o link de acesso do primeiro Webservice é esse. Beleza, essa é a URL. Mas qual seria o namespace e a soap_action?

http://localhost:8080/axis/Calculator.jws?wsdl

Vish… voce nao tem opção de usar algo mais simples como REST? É mais facil consumir JSON através de um HttpPost e HttpGet (da Apache já na API do Android) por exemplo.

Use EJB 3, por que esse artigo é antigo e pede para usar:

É bem mais complicado.

Olha, a opção que eu tenho é a opção de implementar um Webservice em Android até sexta-feira, senão perderei uma oportunidade de emprego!

Eu não sei o que é Rest, nem Json, nem Ejb. O que eu sei é que preciso implementar um Webservice em Android. Só isso.

Eu tenho a opção de aprender 3 conceitos novos em dias, ou de aprender como adaptar os 2 exemplos que postei. O que dá menos trabalho? Só o tempo dirá.

Sigo tentando descobrir a solução para meu problema. O Google não está ajudando. Mas não desistirei.

Veja se esse link te ajuda

http://www.guj.com.br/java/264022-consumir-webservice-com-android

Abraço

Cara, dá uma olhada nessa implementação:

https://github.com/lucasaquiles/SACIBiblio

pode te servir como modelo de como consumir um serviço Rest. Criei uma outra app usando VRaptor pra receber as requisições da app android e retornar o Json que app android iria consumir.

tu também vai precisar da lib http://code.google.com/p/google-gson/ pra converter objetos java em Json e vice-versa.

Olá pessoal!

Enfim consegui fazer um Webservice funcionar em Android. O que eu fiz para isso funcionar? Primeiramente, o código do Webservice que foi publicado com Tomcat e Axis é o seguinte:

public class Calculator {
	public int somar(int numA, int numB) {
		return numA + numB;
	}

	public int subtrair(int numA, int numB) {
		return numA - numB;
	}

	public int multiplicar(int numA, int numB) {
		return numA * numB;
	}

	public int dividir(int numA, int numB) {
		if (numB != 0)
			return numA / numB;
		return 0;
	}
}

Eu o publiquei seguindo o tutorial abaixo:

http://imasters.com.br/artigo/1863/java/web_services_in_java/

No meu projeto Android, criei apenas 2 classes, que listam no Logcat o resultado do Webservice. A classe que realiza a conexão com o Webservice é a classe abaixo:

package br.com.android.projeto;

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

public class WebService {
	private static final String NAMESPACE = "com.service.Calculator";
	private static final String URL = "http://192.168.1.2:8080/axis/Calculator.jws?wsdl";
	private static final String SOAP_ACTION = "Calculator";
	
	public String webServiceOperacao(String operacao, Integer numA, Integer numB) {
		SoapObject request = new SoapObject(NAMESPACE, operacao);
		SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
		String retorno = null;
		// Adiciona parâmetros
		request.addProperty("numA", numA);
		request.addProperty("numB", numB);
		envelope.setOutputSoapObject(request);
		HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
		try {
			androidHttpTransport.call(SOAP_ACTION, envelope);
			SoapObject resultsRequestSOAP = (SoapObject) envelope.bodyIn;
			retorno = resultsRequestSOAP.toString();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return retorno;
	}
}

Reparem que as dúvidas que eu tinha no início desse tópico (o que escrever na constante NAMESPACE, etc) foram solucionadas.

A Activity que imprime o resultado desse Webservice no Logcat é a classe abaixo:

package br.com.android.projeto;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends Activity {
	private WebService ws = new WebService();
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        operacao("somar", 2, 2, "Somar");
        operacao("subtrair", 6, 3, "Subtrair");
        operacao("multiplicar", 7, 3, "Multiplicar");
        operacao("dividir", 21, 3, "Dividir");
	}
    
    public void operacao(String operacao, Integer numA, Integer numB, String texto) {
    	operacao = ws.webServiceOperacao(operacao, numA, numB);
    	// Filtra o valor retornado pelo WebService
    	String[] primeiraParte = operacao.split("=");
    	String[] segundaParte = primeiraParte[1].split(";");
        String retorno = segundaParte[0];
    	Log.i("webservice", texto + ": " + Integer.valueOf(retorno));
    }
}

Porém, agora que consegui um Webservice funcional em Android, decidi ir para o que realmente interessa, que é listar informações de uma tabela de Banco de Dados, via Android. O que eu realmente preciso é criar uma aplicação em Android que busque de um Webservice informações sobre um Banco de Dados Sqlite, rodando em Windows (através da IDE SQLite Expert Personal). Ou seja, o aplicativo em Android acessará um Webservice, que por sua vez listará todos os registros (select * from tabela) de uma determinada tabela de um repositório localizado na mesma máquina que está rodando o Webservice. Logo, não usarei mais um Webservice como o que listei no início do tópico (que soma, subtrai, multiplica e divide). Usarei um Webservice bem mais complexo.

O novo Webservice que criei, afim de acessar um Banco de Dados Sqlite, rodando na minha máquina, é o seguinte:

import java.io.Serializable;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class Dao {
	// Primeira sub-classe
	public class Conexao {

		public Conexao() {
			try{
				// Carregando classe que será utilizada como conector
				Class.forName("org.sqlite.JDBC").newInstance();
			} catch(ClassNotFoundException e){
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		
		private Connection connection;
		
		public Connection getConnection(){
			if (connection == null){
				try{
					// Tentando realizar conexão
					connection = DriverManager.getConnection("jdbc:sqlite:/databases/repositorio_pergunta");
					System.out.println("Conexão ok.");
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			return connection;
		}
		
		public void fecharConexao(){
			try{
				connection.close();
				System.out.println("Conexão fechada.");
			} catch (SQLException e){
				e.printStackTrace();
			}
		}
	}
	
	public String listarPerguntas(Integer inteiro) throws Exception {
		String retorno = null;
		try {
			// 1. Consultar dados
			Conexao con = new Conexao();
			PreparedStatement pst = con.getConnection().prepareStatement(
				"select id, texto, resposta from pergunta");
			// 2. Executar consulta
			pst.execute();
			// 3. Processar resultado
			ResultSet rs = pst.getResultSet();
			while (rs.next()){
				retorno += rs.getInt("id") + ";";
				retorno += rs.getString("texto") + ";";
				retorno += rs.getInt("resposta") + ";";
				retorno += "#";
			}
			// 4. Fechar tudo
			rs.close();
			pst.close();
		} catch (SQLException e) {
			System.out.println("Erro ao listar Pergunta.");
			e.printStackTrace();
		}
		return retorno;
	}
}

Ou seja, ele apenas lista todos os registros da tabela Pergunta, e os concatena em uma string, que será retornada. Decidi retornar uma string para não ter que lidar com tipos complexos (o ideal seria retornar um List no método listarPerguntas).

A tabela Pergunta possui os seguintes campos abaixo:

create table pergunta (
       id integer primary key autoincrement not null,
       texto text not null,
       resposta integer not null
);

Atualmente ela está populada com 10 registros. O campo resposta é um Integer porque no Sqlite não existe tipo Boolean. Neste campo, 1 indica TRUE, e 0 indica FALSE.

A classe do Android que acessa esse novo Webservice é mostrada abaixo:

package br.com.android.projeto;

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

public class WebService {
	private static final String NAMESPACE = "com.service.Dao";
	private static final String URL = "http://192.168.1.2:8080/axis/Dao.jws?wsdl";
	private static final String SOAP_ACTION = "Dao";
	
	public String webServiceBanco() {
		SoapObject request = new SoapObject(NAMESPACE, "listarPerguntas");
		SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
		String retorno = null;
		// Adiciona parâmetros
		envelope.setOutputSoapObject(request);
		HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
		try {
			androidHttpTransport.call(SOAP_ACTION, envelope);
			SoapObject resultsRequestSOAP = (SoapObject) envelope.bodyIn;
			retorno = resultsRequestSOAP.toString();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return retorno;
	}
}

E a Activity que mostra no Logcat o resultado dessa listagem é mostrada abaixo.

package br.com.android.projeto;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends Activity {
	private WebService ws = new WebService();
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        String retorno = ws.webServiceBanco();
        Log.i("webservice", "Retorno: " + retorno);
	}
}

Porém, ao executar este novo Webservice, alguns erros aparecem:

[list]
02-14 18:33:29.805: I/ActivityManager(68): Starting activity: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=br.com.android.projeto/.MainActivity }
02-14 18:33:30.155: W/System.err(304): java.net.SocketException: Permission denied
02-14 18:33:30.195: W/System.err(304): at org.apache.harmony.luni.platform.OSNetworkSystem.createStreamSocketImpl(Native Method)
02-14 18:33:30.195: W/System.err(304): at org.apache.harmony.luni.platform.OSNetworkSystem.createStreamSocket(OSNetworkSystem.java:186)
02-14 18:33:30.225: W/System.err(304): at org.apache.harmony.luni.net.PlainSocketImpl.create(PlainSocketImpl.java:265)
02-14 18:33:30.225: W/System.err(304): at java.net.Socket.checkClosedAndCreate(Socket.java:873)
02-14 18:33:30.245: W/System.err(304): at java.net.Socket.connect(Socket.java:1020)
02-14 18:33:30.245: W/System.err(304): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection.(HttpConnection.java:62)
02-14 18:33:30.255: W/System.err(304): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnectionPool.get(HttpConnectionPool.java:88)
02-14 18:33:30.255: W/System.err(304): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getHTTPConnection(HttpURLConnectionImpl.java:927)
02-14 18:33:30.255: W/System.err(304): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:909)
02-14 18:33:30.255: W/System.err(304): at org.ksoap2.transport.ServiceConnectionSE.connect(Unknown Source)
02-14 18:33:30.285: W/System.err(304): at org.ksoap2.transport.HttpTransportSE.call(Unknown Source)
02-14 18:33:30.285: W/System.err(304): at br.com.android.projeto.WebService.webServiceBanco(WebService.java:21)
02-14 18:33:30.315: W/System.err(304): at br.com.android.projeto.MainActivity.onCreate(MainActivity.java:14)
02-14 18:33:30.315: W/System.err(304): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
02-14 18:33:30.315: W/System.err(304): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2627)
02-14 18:33:30.315: W/System.err(304): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2679)
02-14 18:33:30.315: W/System.err(304): at android.app.ActivityThread.access$2300(ActivityThread.java:125)
02-14 18:33:30.315: W/System.err(304): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2033)
02-14 18:33:30.315: W/System.err(304): at android.os.Handler.dispatchMessage(Handler.java:99)
02-14 18:33:30.325: W/System.err(304): at android.os.Looper.loop(Looper.java:123)
02-14 18:33:30.325: W/System.err(304): at android.app.ActivityThread.main(ActivityThread.java:4627)
02-14 18:33:30.325: W/System.err(304): at java.lang.reflect.Method.invokeNative(Native Method)
02-14 18:33:30.355: W/System.err(304): at java.lang.reflect.Method.invoke(Method.java:521)
02-14 18:33:30.355: W/System.err(304): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
02-14 18:33:30.355: W/System.err(304): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
02-14 18:33:30.355: W/System.err(304): at dalvik.system.NativeStart.main(Native Method)
02-14 18:33:30.355: I/webservice(304): Retorno: null
02-14 18:33:30.455: I/ActivityManager(68): Displayed activity br.com.android.projeto/.MainActivity: 551 ms (total 551 ms)
[/list]

O erro SocketException, creio eu, significa que tenho que fazer algo para que eu consiga acessar um Webservice que acessa um DAO. Mas que algo seria isso?

Logo, eu consigo acessar um Webservice simples em Android. Mas acessar outro que retorna dados de um Banco de Dados? Não!

Alguém sabe o que devo mudar na classe WebService.java para que meu aplicativo funcione?

Acabei de modificar a classe WebService.java para o modelo abaixo:

package br.com.android.projeto;

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

public class WebService {
	private static final String NAMESPACE = "com.service.Dao";
	private static final String URL = "http://10.0.82.70:8080/axis/Dao.jws?wsdl";
	private static final String SOAP_ACTION = "Dao";
	
	public String webServiceBanco() {
		SoapObject request = new SoapObject(NAMESPACE, "listarPerguntas");
		SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
		String retorno = null;
		// Adiciona parâmetros
		envelope.setOutputSoapObject(request);
		HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
		try {
			androidHttpTransport.call(SOAP_ACTION, envelope);
			retorno = envelope.bodyIn.toString(); 
		} catch (Exception e) {
			e.printStackTrace();
		}
		return retorno;
	}
}

Com este modelo, o erro que aparece não é mostrado como um erro, mas sim como um retorno do WebService. E ele diz:

02-14 22:46:17.772: I/webservice(445): Retorno: SoapFault - faultcode: ‘soapenv:Server.userException’ faultstring: ‘java.lang.NullPointerException’ faultactor: ‘null’ detail: org.kxml2.kdom.Node@44e9a1b0

(Abaixo a listagem de saída da Activity principal do programa):

[list]
02-14 22:46:16.842: I/AndroidRuntime(438): NOTE: attach of thread ‘Binder Thread #3’ failed
02-14 22:46:17.772: I/webservice(445): Retorno: SoapFault - faultcode: ‘soapenv:Server.userException’ faultstring: ‘java.lang.NullPointerException’ faultactor: ‘null’ detail: org.kxml2.kdom.Node@44e9a1b0
02-14 22:46:17.972: I/ActivityManager(58): Displayed activity br.com.android.projeto/.MainActivity: 1247 ms (total 1247 ms)
[/list]

Eu já esgotei as possibilidades de busca desses erros no Google, mas achei nada! :frowning:

Alguém sabe como dar jeito na minha aplicação? Como fazer o erro acima parar?

Ou ao menos um tutorial realmente fácil para acesso de banco de dados (via WebService) em Android (usando Ksoap)?

Nem que seja um WebService que retorna 1 mísero campo de um banco de dados? Será que tem como acessar um banco de dados, em Android, via intermédio de WebService?

Esse Tutorial aaqui ta bem simples e fácil de entender…o indicado pra android é usar Json…dá uma olhada.

http://www.euandroid.com.br/tutoriais/tutorial-dev/2011/07/criando-aplicacao-cliente-x-servidor-com-android-j2ee-e-json/

Eu andei conversando com uns colegas, e todos me falaram que o que é usado hoje em dia é RESTful e JSON. Bom, pelo jeito não adianta mesmo: eu terei que jogar fora tudo que fiz e recomeçar do zero, usando essas tecnologias.

Tenho até sexta-feira pra fazer isso! Espero conseguir.

Vai indo que da para fazer nesse tempo sim.

Consegui!

Graças a ajuda do Jonathan Coelho. Obrigado!

Vou atualizar esse tópico para “[RESOLVIDO]”.