Uma tarefa muito comum do dia a dia é realizar requisições HTTP e consumir Web Services, mas às vezes pode ser um tanto quanto chata pois precisa de configurações e alguns passos a serem seguidos.
Para exemplo do post, vamos consumir um serviço para buscas de CEP. Os passos que deveríamos seguir são:
- Desenhar o layout
- Consumir nosso serviço
- Mostrar os dados consumidos para o usuário
Após concluir todos esses passos teremos nossa app funcionando da seguinte maneira:
Desenhando nossa app
O primeiro passo será criar o layout da nossa app. Como o assunto do post refere-se ao web service, vou disponibilizar o layout:
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="50dp"
android:layout_width="match_parent"
tools:context="br.com.matheuscastiglioni.blog.requisicao_http.MainActivity">
<EditText
android:digits="0123456789"
android:layout_height="wrap_content"
android:hint="CEP"
android:id="@+id/etMain_cep"
android:textColor="#595959"
android:textSize="25sp"
android:inputType="number"
android:layout_width="match_parent"/>
<LinearLayout
android:gravity="center"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_width="match_parent">
<Button
android:background="@color/colorPrimary"
android:layout_height="wrap_content"
android:id="@+id/btnMain_buscarCep"
android:layout_marginBottom="10dp"
android:padding="10dp"
android:text="Buscar CEP"
android:textColor="#FDFDFD"
android:textSize="22sp"
android:layout_width="wrap_content"/>
<TextView
android:layout_height="match_parent"
android:id="@+id/etMain_resposta"
android:textColor="#595959"
android:textSize="20sp"
android:layout_width="match_parent"/>
Após adicionar esse layout para a activity, devemos ter nossa app parecida com:
Consumindo um serviço
Agora que o layout está pronto, podemos começar a consumir nosso serviço. Para isso alguns passos devem ser seguidos, sendo eles:
- Adicionar um listener no botão Buscar CEP
- Validar se o CEP foi digitado
- Realizar a busca do CEP
- Mostrar os dados para o usuário
Adicionando um listener no botão
Primeiro vamos começar adicionando um listener em nosso botão, para quando ele for clicado realizar alguma função, mas como podemos fazer isso? Para trabalhar com eventos de click em botões podemos utilizar o setOnClickListener
.
Vamos buscar nosso botão e atribuí-lo em uma variável:
Button btnBuscarCep = findViewById(R.id.btnMain_buscarCep);
Agora precisamos adicionar o listener no botão:
btnBuscarCep.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// buscar o CEP
}
});
Pronto, com isso já devemos ser capazes de escutar e emitir alguma função quando o botão for clicado.
Criando a classe para buscar o CEP
Para realizar a busca do CEP vamos criar uma classe responsável por esse requisição HTTP:
public class HttpService {
}
Vou chamar a classe de HttpService
pois a mesma vai consumir um serviço HTTP. Aqui entra um detalhe, toda requisição HTTP deve ser feita em background pelo Android, ou seja, a mesma não pode ser feita na thread principal, mas por que isso ocorre?
Entendendo como uma requisição HTTP funciona
Uma requisição HTTP é feita da seguinte maneira:
- Primeiro devemos configurar a requisição (URL, cabeçalhos e resposta).
- Depois de configurada podemos realizar a requisição.
- Durante o processo de requisição devemos aguardar o servidor responsável processá-la.
- Após o servidor processá-la podemos pegar o retorno.
Veja que no terceiro passo não sabemos quanto tempo o servidor levará para conseguir processar a requisição e devolver, por isso o Android exige que a requisição seja feita em background, assim o app não irá travar ou ficar congelado para o usuário enquanto a requisição é realizada.
Beleza Matheus, agora sabemos o motivo do Android exigir que a requisição seja feita em background ou em segundo plano, mas como podemos fazer isso?
Adaptando nossa classe
O primeiro passo será extender a classe AsyncTask
do Android responsável por realizar tarefas em background.
public class HttpService extends AsyncTask<Void, Void, CEP> {
}
Opa, espera ae Matheus, que doidera é essa de Void, Void, CEP
?
Parâmetros do AsyncTask
Calma, vamos devagar, para tudo tem uma explicação, sempre que extendemos a AsyncTask
devemos passar esses três parâmetros para ela, sendo eles:
- Primeiro: Será o tipo de parâmetro enviado para a execução da classe
- Segundo: Será o tipo de parâmetro recebido no método
onProgressUpdate
(não iremos utilizá-lo, o mesmo é chamado sempre que a requisição é atualizada, ideal para barras de progresso) - Terceiro: Será o tipo de retorno da classe
Criando o modelo CEP
Repare que o terceiro parâmetro da AsyncTask
é uma classe CEP
, mas ainda não temos ela, então vamos criá-la:
public class CEP {
private String cep;
private String logradouro;
private String complemento;
private String bairro;
private String cidade;
private String estado;
public String getCep() {
return cep;
}
public void setCep(String cep) {
this.cep = cep;
}
public String getLogradouro() {
return logradouro;
}
public void setLogradouro(String logradouro) {
this.logradouro = logradouro;
}
public String getComplemento() {
return complemento;
}
public void setComplemento(String complemento) {
this.complemento = complemento;
}
public String getBairro() {
return bairro;
}
public void setBairro(String bairro) {
this.bairro = bairro;
}
public String getCidade() {
return cidade;
}
public void setCidade(String cidade) {
this.cidade = cidade;
}
public String getEstado() {
return estado;
}
public void setEstado(String estado) {
this.estado = estado;
}
@Override
public String toString() {
return "CEP: " + getCep()
+ "nLogradouro: " + getLogradouro()
+ "nComplemento: " + getComplemento()
+ "nBairro: " + getBairro()
+ "nCidade:" + getCidade()
+ "nEstado: " + getEstado();
}
}
Veja que se trata apenas de uma classe para representar e armazenar as informações de nosso CEP, não tem segredo.
Bom, agora que já extendemos a classe AsyncTask
e conseguimos entender seus parâmetros, devemos sobrescrever o método responsável pela execução em background:
Realizando a requisição em background
O único método que somos obrigados a sobrescrever quando extendemos de AsyncTask
é o doInBackground
, como o próprio nome já diz, será o método responsável por realizar a requisição para nosso web service em background.
@Override
protected CEP doInBackground(Void... voids) {
// realizar requisição e consumir o serviço
}
Para realizar a requisição precisamos de um CEP, mas em nossa classe não recebemos ele ainda, como podemos resolver o problema? Se nossa classe HttpService
precisa de um CEP para funcionar, porque não passá-lo pelo construtor? Assim garantimos que sempre ela terá um CEP ao ser instanciada (usada):
private final String cep;
public HttpService(String cep) {
this.cep = cep;
}
Veja que agora já possuímos um CEP para buscar os dados. O primeiro passo para nossa requisição funcionar será validar o CEP digitado e passado para nossa classe:
@Override
protected CEP doInBackground(Void... voids) {
if (this.cep != null && this.cep.length() == 8) {
// realizar busca
}
}
Repare que agora estamos validando se foi passado um CEP e se o mesmo contém oito dígitos.
Configurando e realizando a requisição
Vamos começar a configurar nossa requisição, o primeiro passo é termos uma URL
para consumirmos:
URL url = new URL("https://ws.matheuscastiglioni.com.br/ws/cep/find/" + this.cep + "/json/");
Durante a construção da URL
pode acontecer de passarmos uma que seja inválida ou que não existe, por isso, devemos realizar um tratamento de exceção com try catch
:
try {
URL url = new URL("https://ws.matheuscastiglioni.com.br/ws/cep/find/" + this.cep + "/json/");
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
Agora precisamos abrir uma conexão e configurar os cabeçalhos dela (Tipo de requisição, tipo de retorno, tempo máximo de espera, etc…):
try {
URL url = new URL("https://ws.matheuscastiglioni.com.br/ws/cep/find/" + this.cep + "/json/");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Content-type", "application/json");
connection.setRequestProperty("Accept", "application/json");
connection.setDoOutput(true);
connection.setConnectTimeout(5000);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
Com as configurações realizadas, precisamos de fato, realizar a conexão, ou seja, conectar em nossa url
:
Pronto, já conseguimos conectar, mas não basta conectar e realizar a requisição, precisamos de fato pegar a resposta e salvar em alguma variável, como podemos fazer isso?
Lendo a resposta da requisição
Podemos ler a resposta facilmente com a classe Scanner
do pacote java.io
. Ela abstrai bastante a complexidade de ler informações:
Scanner scanner = new Scanner();
Beleza, estamos criando nosso scanner
, mas de onde ele vai ler as informações? Para isso temos o método openStream
em nossa url
:
Scanner scanner = new Scanner(url.openStream());
Como quase todas as classes do pacote java.io
, devemos tratar a exceção para arquivos não encontrados. Como já temos nosso try catch
, precisamos apenas adicionar mais um catch
em nosso try
:
catch (IOException e) {
e.printStackTrace();
}
Agora, com as exceções tratadas e nosso scanner
recebendo as informações, já podemos realizar a leitura da resposta:
StringBuilder resposta = new StringBuilder();
// if omitido
Scanner scanner = new Scanner(url.openStream());
while (scanner.hasNext()) {
resposta.append(scanner.next());
}
Beleza, tudo certo? Errado, até o momento lemos a resposta e passamos para a classe StringBuilder
, porém, lembra que o terceiro parâmetro de nosso AsyncTask
era do tipo CEP
? Pois é, sendo assim precisaremos retornar um CEP
em nosso método doInBackground
, pois até agora temos um JSON lido dentro de uma String:
{"codibge":3530706,"codestado":35,"cep":"13845-373","logradouro":"Rua Caiapós","complemento":"","bairro":"Jardim Igaçaba","cidade":"Mogi Guaçu","estado":"SP"}
Afinal, como podemos pegar esse JSON, gravar dentro de uma String
e converte-lo para a classe CEP
?
Convertendo dados do json
Realizar a conversão de dados em Java é algo trabalhoso, sabendo isso, a Google lançou uma biblioteca chamada GSON, responsável em abstrair a complexidade na hora de converter dados relacionados com JSON. Para começar a usá-la, devemos declarar a dependência em nosso build.gradle
:
compile 'com.google.code.gson:gson:2.8.2'
Depois de adicionar a dependência, realize o sincronismo do Gradle.
Retornando um CEP
Com nosso GSON instalado, podemos converter nosso JSON para um objeto do tipo CEP da seguinte maneira:
return new Gson().fromJson(resposta.toString(), CEP.class);
Muito simples não?
Agora que nossa classe responsável por realizar a requisição esta pronta, podemos utilizá-la no listener de nosso botão. Caso tenha se perdido em algum passo, segue a classe completa:
public class HttpService extends AsyncTask<Void, Void, CEP> {
private final String cep;
public HttpService(String cep) {
this.cep = cep;
}
@Override
protected CEP doInBackground(Void... voids) {
StringBuilder resposta = new StringBuilder();
if (this.cep != null && this.cep.length() == 8) {
try {
URL url = new URL("https://ws.matheuscastiglioni.com.br/ws/cep/find/" + this.cep + "/json/");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Content-type", "application/json");
connection.setRequestProperty("Accept", "application/json");
connection.setDoOutput(true);
connection.setConnectTimeout(5000);
connection.connect();
Scanner scanner = new Scanner(url.openStream());
while (scanner.hasNext()) {
resposta.append(scanner.next());
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return new Gson().fromJson(resposta.toString(), CEP.class);
}
}
Retornando os dados para o usuário
Bom, até o momento de todos os passos que deveríamos realizar:
- Adicionar um listener no botão Buscar CEP
- Validar se o CEP foi digitado
- Realizar a busca do CEP
- Mostrar os dados para o usuário
Ja concluímos os três primeiros, portanto, precisamos apenas retornar os dados para o usuário.
Buscando os dados do CEP
Vamos começar buscando os dados do CEP digitado em nosso app, para isso já havíamos criado o listener, precisamos apenas fazer uso da nossa classe HttpService
:
final EditText cep = findViewById(R.id.etMain_cep);
final TextView resposta = findViewById(R.id.etMain_resposta);
Button btnBuscarCep = findViewById(R.id.btnMain_buscarCep);
btnBuscarCep.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
CEP retorno = new HttpService(cep.getText().toString()).execute().get();
resposta.setText(retorno.toString());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
});
Com isso iremos ter o seguinte resultado:
Caso tenha ficado alguma dúvida, você pode encontrar o projeto completo aqui.
Postado em blog.matheuscastiglioni.com.br