Vamos utilizar neste post a biblioteca ksoap2 para realizar a integração com o webservice, diversos aplicativos utilizam esta biblioteca para consumir serviços soap, apesar disso não há muito material sobre ela em português, pensando nisso decidi escrever sobre ela. Minha intenção neste post é mostrar como enviar e receber desde um dado primário à uma lista de objetos, ao fim disponibilizarei o link para download dos projetos usados, vou utilizar um webservice em java criado no netbeans 8.0 e a aplicação cliente no android studio, sem mais delongas vamos começar.
Configuração do projeto
Precisaremos colocar a biblioteca ksoap2 no nosso projeto, você pode fazer isso baixando o jar e colocando na mão dentro da pasta lib do projeto, mas usarei o gradle para baixa-la automaticamente para mim, para isso no arquivo “build.gradle” do projeto acrescente o seguinte código:
repositories {
mavenCentral()
maven {
url 'http://ksoap2-android.googlecode.com/svn/m2-repo'
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.0.0'
compile 'com.google.code.ksoap2-android:ksoap2-android:3.3.0'
}
Depois disso o android studio pedirá para sincronizar o projeto e teremos a ksoap2 na versão 3.3.0 como dependência do projeto.
No “AndroidManifest.xml” coloque a permissão para acessar a internet.
[markdown]
`<uses-permission android:name=”android.permission.INTERNET” />`
[/markdown]
Agora já temos a biblioteca e a permissão para podermos consumir serviços da internet. Não entrarei em detalhes de layout ou algo do gênero, pois esse não é o objetivo do post, mãos à obra.
Para fazer chamadas para um serviço em um webservice não podemos utilizar a thread principal do android, precisamos fazer uma chamada assíncrona, isso evita com que a tela do usuário trave enquanto a chamada não é finalizada. No nosso exemplo usaremos a classe AsyncTask.
CHAMADA COM DADO PRIMITIVO
Criei uma classe chamada PrimaryDataTask que estende da classe AsyncTask para fazer uma chamada para nosso webservice enviando um dado do tipo primitivo e recebendo o mesmo tipo de dado como resposta, neste exemplo usarei uma String, mas poderia ser qualquer tipo de dado primitivo como int, boolean e etc., isso irá depender do que o seu webservice está esperando.
public class PrimaryDataTask extends AsyncTask {
private String data;
private String mResponse;
private OnReturnServicePrimary mListener;
public PrimaryDataTask(String data, OnReturnServicePrimary mListener) {
this.data = data;
this.mListener = mListener;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Boolean doInBackground(Void... params) {
final String METHOD_NAME = "getString";
final String SOAP_ACTION = AccessConfig.NAMESPACE + METHOD_NAME;
final String NAMESPACE = AccessConfig.NAMESPACE;
final String URL = AccessConfig.URL;
try {
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
request.addProperty("texto", data);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
//envelope.dotNet = true;
envelope.setOutputSoapObject(request);
HttpTransportSE httpTransport = new HttpTransportSE(URL);
httpTransport.call(SOAP_ACTION, envelope);
SoapPrimitive response = (SoapPrimitive) envelope.getResponse();
mResponse = response.toString();
return true;
} catch (Exception e) {
e.printStackTrace();
Log.e("ERRO", e.toString());
}
return false;
}
@Override
protected void onPostExecute(Boolean sucess) {
if (mListener != null) {
if (sucess) mListener.onCompletion(mResponse);
else mListener.onError();
}
}
public interface OnReturnServicePrimary {
public void onCompletion(String response);
public void onError();
}
}
Implementei dois métodos da classe AsyncTask o “doInBackground” e o “onPostExecute”, o “doInBackground” é o método que usaremos para fazer nossa chamada e é o único método que somos obrigados a usar quando utilizamos a AsyncTask e o “onPostExecute” é o método chamado depois que o “doInBackground” termina sua execução.
No começo do nosso método “doInBackground” faço a configuração para acessar nosso webservice, o “METHOD_NAME” como o nome já diz é o nome do método que desejamos acessar do serviço, o “NAMESPACE” no nosso caso é o nome do pacote chamado “ws” que está a classe que iremos acessar no webservice, o “SOAP_ACTION” é o name space mais o método, e a URL é o endereço do webservice.
Agora criaremos um objeto do tipo “SoapObject” que irá conter o dado que enviaremos, ao instanciar essa classe passo dois parâmetros, o name space e o nome do método, depois adiciono nele através do método “addProperty()” o nome do parâmetro que o serviço está esperando (o nome do parâmetro precisa estar exatamente igual ao nome colocado no webservice) e a nossa variável propriamente dita. Logo após criaremos um “SoapSerializationEnvelope” e passaremos a constante SoapEnvelope.VER11 de parâmetro ao instanciar a classe, se você utilizará um webservice em dotNet será necessário avisar o seu envelope com:
envelope.dotNet = true;
Como esse não é o nosso caso não utilizarei essa linha de código, e depois vou colocar meu “request” no “envelope” que fará a serialização das informações que serão enviadas. Agora criarei a chamada com as informações que já tenho com a classe HttpTransportSE e passarei no seu construtor a url do webservice, depois chamarei o método “call()” desta classe e passarei os seguintes parâmetros o “SOAP_ACTION” e o “envelope”. Pronto, a chamada foi realizada, se tudo estiver certo as informações foram enviadas para o webservice e agora precisamos obter a resposta dele, para isso vou criar uma variável chamada “response” do tipo “SoapPrimitive” e nela vou armazenar o retorno do webservice chamando o método “getResponse()” da minha variável “envelope”. No meu “onPostExecute” faço uma verificação para saber se a minha “Activity” que chamar minha “PrimaryDataTask” está implementando minha interface “OnReturnServicePrimary”, se não deu nenhum erro eu chamo o método “onCompletion” da minha interface passando a “String” com o retorno do webservice caso contrário chamo o “onError”. Vamos ver como utilizei essa classe na minha “Activity”.
Não vou me ater nos detalhes da minha “Activity”, criei uma classe chamada “PrimaryDataActivity” que estende de “Activity” sem muitas surpresas até aqui, criarei um método chamado “soapService()” que não retornará nada e o chamarei no evento de click de um botão, veja-o a seguir:
private void soapService() {
data = editTextValue.getText().toString();
new PrimaryDataTask(data, new PrimaryDataTask.OnReturnServicePrimary() {
@Override
public void onCompletion(String response) {
textViewResponse.setText(response);
}
@Override
public void onError() {
Toast.makeText(getBaseContext(), "Ocorreu um erro.", Toast.LENGTH_SHORT).show();
}
}).execute();
}
Pego o dado digitado no meu “EditText” e passo para minha variável “data”, depois disso instancio minha classe “PrimaryDataTask”, o construtor desta classe espera uma “String” e a interface “OnReturnServicePrimary”, primeiro passo a minha variável “data” com que o usuário digitou e depois instancio minha interface e utilizo os dois métodos que vem com ela, no “onCompletion()” utilizo o “response” e coloco no meu “TextView” para mostrar para o usuário o retorno do servidor, caso de um erro mostro um “Toast” na tela informando que ouve um erro na chamada.
CHAMADA COM ARRAY DE STRING
Daqui em diante tratarei só das diferenças que as classes sofreram com as alterações dos tipos de chamadas, já que não haverá mudanças drásticas.
Criaremos uma classe “ArrayStringsTask” aos moldes da “PrimaryDataStringTask”, no construtor dessa minha classe receberemos um array de strings e a nossa interface.
public ArrayStringsTask(String[] lista, OnReturnServiceArrayStrings mListener) {
this.lista = lista;
this.mListener = mListener;
}
Vamos precisar alterar o método que acessaremos no serviço, como será uma chamada no mesmo webservice iremos mudar somente o “METHOD_NAME”.
final String METHOD_NAME = "getArrayString";
Para adicionar a minha lista de strings no meu “SoapObject” usarei um for-each.
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
for (String item : lista) {
request.addProperty("lista", item);
}
A resposta do nosso serviço será um array de strings, para pegarmos esse retorno criaremos um response com a classe “Vector” pegando o “getResponse()” do nosso envelope como havíamos feito antes, depois criarei um array de strings com o tamanho do vector que veio no retorno do meu serviço e, para finalizar faremos um for para popular nosso array de strings, veja como o código ficou.
Vector response = (Vector) envelope.getResponse();
mResponse = new String[response.size()];
for (int i = 0; i < response.size(); i++) {
mResponse[i] = response.get(i).toString();
}
Nossa “Activity” não terá muitas novidades, a única diferença é que neste caso vou popular um “ListView” com o retorno da lista do serviço, você poderá ver com maiores detalhes essa implementação baixando o projeto.
CHAMADA COM OBJETO
No nosso exemplo criaremos uma classe chamada “Aluno” com atribuições bem simples, mas precisaremos implementar dentro desta nossa classe a interface “KvmSerializable” que será responsável por serializar nosso objeto, obrigatoriamente teremos que sobrescrever seus quatro métodos, “getProperty()”, “getPropertyCount()”, ”setProperty()” e ”getPropertyInfo()”. Detalhe importante é que as propriedades do seu objeto precisam ter o mesmo tipo e o mesmo nome das propriedades do objeto que existe no webservice, lembrando que será diferenciado letras maiúsculas de minúsculas.
public class Aluno implements KvmSerializable {
private Integer id;
private String nome;
private String curso;
@Override
public Object getProperty(int index) {
switch (index) {
case 0:
return id;
case 1:
return nome;
case 2:
return curso;
}
return null;
}
@Override
public int getPropertyCount() {
return 3;
}
@Override
public void setProperty(int index, Object value) {
switch (index) {
case 0:
this.id = Integer.parseInt(value.toString());
break;
case 1:
this.nome = value.toString();
break;
case 2:
this.curso = value.toString();
break;
default:
break;
}
}
@Override
public void getPropertyInfo(int index, Hashtable properties, PropertyInfo info) {
switch (index) {
case 0:
info.type = PropertyInfo.INTEGER_CLASS;
info.name = "id";
break;
case 1:
info.type = PropertyInfo.STRING_CLASS;
info.name = "nome";
break;
case 2:
info.type = PropertyInfo.STRING_CLASS;
info.name = "curso";
break;
}
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getCurso() {
return curso;
}
public void setCurso(String curso) {
this.curso = curso;
}
}
O método “getProperty()” retorna a propriedade serializada em um índice especificado, o “getPropertyCount()” retorna o número de propriedades serializáveis, o “setProperty()” define um valor para uma propriedade do objeto através do índice informado e o “getPropertyInfo” retorna uma informação da propriedade a partir dos parâmetros passados.
Falando somente das diferenças na nossa classe AlunoTask como já esperado o construtor agora espera receber um “Aluno” como parâmetro.
public AlunoTask(Aluno aluno, OnReturnServiceAluno mListener) {
this.aluno = aluno;
this.mListener = mListener;
}
O método do serviço será outro também.
final String METHOD_NAME = "getAluno";
No caso de trabalhar com objetos, logo após criarmos o nosso “SoapObject” vamos criar um “PropertyInfo” com nome “pi” que usaremos para armazenar nosso objeto e colocar no nosso “SoapObject”.
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
PropertyInfo pi = new PropertyInfo();
pi.setName("aluno");
pi.setValue(aluno);
pi.setType(aluno.getClass());
request.addProperty(pi);
O retorno do serviço usaremos o “SoapObject” chamando o seu método “getProperty” passando o nome da propriedade e colocando no nosso objeto “aluno”.
SoapObject response = (SoapObject) envelope.getResponse();
aluno.setId(Integer.parseInt(response.getProperty("id").toString()));
aluno.setNome(response.getProperty("nome").toString());
aluno.setCurso(response.getProperty("curso").toString());
Na minha “AlunoActivity” é mais do mesmo.
CHAMADA COM ARRAY DE OBJETOS
Na nossa classe ArrayAlunosTask nosso construtor receberá uma lista de alunos e a nossa interface listener. Vamos alterar o método que vamos chamar do webservice.
final String METHOD_NAME = "getListAlunos";
Para montar nosso “SoapObject” que será enviado é igual ao nosso outro exemplo de array de strings.
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
for (Aluno aluno : lista) {
request.addProperty("lista", aluno);
}
No retorno do webservice utilizaremos um “Vector” pegando o “getResponse” do nosso envelope, faremos um for pegando o objeto em cada posição do vetor dando cast para um “SoapObject”, depois pegamos cada propriedade do objeto e passamos para nosso objeto “Aluno” através do “getProperty” como já vimos anteriormente. Finalizamos adicionando cada aluno em um array de alunos.
Vector responseVector = (Vector) envelope.getResponse();
for (int i = 0; i < responseVector.size(); i++) {
SoapObject soapObject = (SoapObject) responseVector.get(i);
Aluno aluno = new Aluno();
aluno.setId(Integer.parseInt(soapObject.getProperty("id").toString()));
aluno.setNome(soapObject.getProperty("nome").toString());
aluno.setCurso(soapObject.getProperty("curso").toString());
mResponse.add(aluno);
}
Os projetos estão disponíveis no github, clique aqui.
Qualquer dúvida ou sugestão deixem nos comentários, espero ter ajudado!