Continuando os posts sobre consumir web service’s no Android, no post Consumindo Web Service no Android, não foi utilizado nenhuma biblioteca para auxiliar o consumo, realizamos a conexão, pegamos e tratamos a resposta tudo manualmente.

Porém nesse post, iremos melhorar o exemplo dado anteriormente, portanto, caso ainda não tenha lido o post anterior recomendo que vá até ele e faça a leitura antes de continuar o atual.

Podemos ver que consumir serviços é uma tarefa muito comum do dia a dia, pensando nisso, será que existe alguma maneira de simplificarmos essa tarefa? Sim, pensando nisso a Square criou o Retrofit, uma biblioteca open source que transforma toda aquela parte chata de consumir um simples serviço em uma facilidade tremenda.

Gif animado minions

Começando com o Retrofit

Com o Retrofit conseguimos realizar o consumo de serviços fácilmente, a unica necessidade é realizar algumas configurações, depois de prontas tudo fica tranquilo.

Instalando o Retrofit

O primeiro passo que devemos realizar é adicionar o Retrofit como dependência de nosso projeto:

compile 'com.squareup.retrofit2:retrofit:2.3.0'

Adicione em seu build.gradle referente a app e espere o Gradle baixar e sincronizar a nova dependência de nosso projeto.

Configurando o Retrofit

O primeiro passo será criar uma classe para ser responsável em configurar e instânciar o Retrofit para podermos utilizá-lo, se trata de uma classe referente a configuração e estamos configurando Retrofit, nada melhor que RetrofitConfig para o nome:

public class RetrofitConfig {

}

A configuração do Retrofit será feita durante a inicialização da classe, sendo assim, temos que configurá-lo no construtor da mesma:

public RetrofitConfig() {
	// aqui vem a configuração
}

Mas afinal, quais configurações devemos fazer Matheus? Pois é, ainda não comentei, os passos que deveremos seguir são:

Começando a configuração

Precisamos de um objeto do Retrofit, mas como podemos criá-lo? Para começarmos a configurá-lo temos o seu Builder, bem comum para construções de objetos:

public RetrofitConfig() {
	new Retrofit.Builder();
}

Configurando a URL do serviço

Para definir a URL que será utilizada no web service temos o método baseUrl:

public RetrofitConfig() {
	new Retrofit.Builder().baseUrl("http://ws.matheuscastiglioni.com.br/ws/");
}

Aqui tem um detalhe, a URL deve sempre terminar com / senão iremos ter uma Exception ao tentar utilizá-lo.

Setando o conversor

Como mencionado no segundo passo, precisamos converter a resposta que é um JSON em formato de String:

{"codibge":3530706,"codestado":35,"cep":"13845-373","logradouro":"Rua Caiapós","complemento":"","bairro":"Jardim Igaçaba","cidade":"Mogi Guaçu","estado":"SP"}

Para um objeto do tipo CEP, então devemos setar quem será o responsável por realizar essa conversão, afinal, não queremos ter que ficar nos preocupando em converter dados de resposta, queremos apenas consumir o serviço.

Conheça o Jackson

Assim como no primeiro post utilizamos o GSON, aqui também iremos utilizar uma outra biblioteca responsável em realizar a nossa conversão, porém, não iremos utilizar o já mencionado GSON, mas se o mesmo já foi utilizado então porque você não o utiliza novamente Matheus? Para trabalhar junto com o Retrofit eu prefiro o Jackson.

Instalando o Jackson

Como já fizemos anteriormente com o Retrofit, também devemos declarar a dependência do Jackson em nosso projeto:

compile 'com.squareup.retrofit2:converter-jackson:2.3.0'

Basta adicionar essa linha logo após a dependência do Retrofit, após isso, o Gradle novamente irá realizar o download e sincronizar o Jackson com o nosso projeto.

Configurando o Jackson

Continuando o segundo passo, devemos agora configurar o Retrofit para trabalhar com o Jackson, podemos fazer isso através do método addConverterFactory que recebe a classe que será responsável por lidar com a conversão:

public RetrofitConfig() {
	this.retrofit = new Retrofit.Builder()
			.baseUrl("http://ws.matheuscastiglioni.com.br/ws/")
			.addConverterFactory(JacksonConverterFactory.create());
}

Criando o Retrofit

E por último não menos importante, devemos criar finalmente o objeto Retrofit para utilizarmos em nosso projeto, para isso temos o método build:

public RetrofitConfig() {
	new Retrofit.Builder()
			.baseUrl("http://ws.matheuscastiglioni.com.br/ws/")
			.addConverterFactory(JacksonConverterFactory.create())
			.build();
}

Mas afinal, onde esta nosso objeto Retrofit? Para armazená-lo vamos criar um atributo em nossa classe, que será setado assim que a mesma for instanciada:

public class RetrofitConfig {

    private final Retrofit retrofit;

    public RetrofitConfig() {
        this.retrofit = new Retrofit.Builder()
                .baseUrl("http://ws.matheuscastiglioni.com.br/ws/")
                .addConverterFactory(JacksonConverterFactory.create())
                .build();
    }

}

Pronto, até o momento já temos nosso Retrofit configurado, vamos agora consumir nosso web service

Configurando nosso serviço

O Retrofit trabalha com serviços(services), para criar um serviço(service) precisamos configurá-lo e seguir alguns passos, assim como fizemos com o Retrofit:

Criando o serviço

Para criar um serviço, precisamos apenas de uma interface, se o serviço trata-se de um CEP, então nada melhor do que CEPService:

public interface CEPService {

}

Pronto, já temos nosso serviço criado, precisamos agora ir para o segundo passo e criar a requisição(método) que ele irá realizar.

Criando a requisição

Como pretendemos buscar as informações desse CEP, podemos definir a requisição como buscarCEP:

public interface CEPService {

    buscarCEP();

}

Beleza Matheus, mas isso é apenas uma interface normal do Java, onde esta toda a configuração que você mencionou anteriormente? Calma, vamos devargazinho que a gente chega no pódio kk

Definindo o verbo

Nosso serviço irá buscar, ou seja, pegar, que seria GET em inglês, mas como podemos definir isso?

public interface CEPService {

    @GET
    buscarCEP();

}

Pronto, agora definimos ao serviço que a requisição trata-se do verbo GET.

Definindo a URL

Já definimos a requisição, definimos o verbo, mas para onde será realizada a requisição? Sim, o Retrofit não tem bola de cristal para adivinhar, precisamos informar para onde será feito o GET, novamente, como podemos fazer isso?

public interface CEPService {

    @GET("cep/find/{cep}/json")
    buscarCEP();

}

Veja que informamos a URL dentro dos parenteses do GET, observe também que informamos apenas a parte relativa a URL base configurada no Retrofit baseUrl("http://ws.matheuscastiglioni.com.br/ws/"), sendo assim a URL final será: "http://ws.matheuscastiglioni.com.br/ws/cep/find/{cep}/json".

Mas Matheus, porque você definou o CEP como {cep}? Eu sei, você é apressado, esta pensando em várias possibilidades e tenho certeza que acertou, é nesse trecho que vamos informar um parâmetro.

Definindo os parâmetros

Como mencionado anteriormente, veja que em nossa URL cep/find/{cep}/json informamos uma lacuna {cep}, é nela que deve entrar o CEP informado pelo usuário, mas como vamos substituir a lacuna por um valor:

public interface CEPService {

    @GET("cep/find/{cep}/json")
	buscarCEP(String cep);

}

Será que apenas receber o CEP como parâmetro de nosso método é suficiente? Se você achou que sim sinto muito lhe dizer mas esta errado, precisamos mapear a qual lacuna esse parâmetro pertence, realizamos esse mapeamento através do @Path seguido pelo nome da lacuna:

public interface CEPService {

    @GET("cep/find/{cep}/json")
    buscarCEP(@Path("cep") String cep);

}

Pronto, agora sim, o valor passado para o parâmetro CEP será preenchido na lacuna {cep}, se nossa lacuna chamasse {nome}, então nosso @Path seria nome.

Definindo o retorno

Até agora tenho certeza que sua classe não esta compilando, afinal não informamos o retorno do método, mas, qual será o retorno? O Retrofit trabalha com Call's, então precisamos apenas informá-la?

public interface CEPService {

    @GET("cep/find/{cep}/json")
    Call buscarCEP(@Path("cep") String cep);

}

Aqui entra outro detalhe, lembra que adicionamos um conversor de nossa resposta para um objeto do tipo CEP, pois é, a Call é uma classe genérica, portanto, precisamos avisá-la qual será o retorno esperado, assim o Jackson entra em ação e tentar converter a nossa resposta automaticamente para o tipo(objeto) de retorno informado, mas como podemos falar para nossa Call o que ela deve retornar?

public interface CEPService {

    @GET("cep/find/{cep}/json")
    Call<CEP> buscarCEP(@Path("cep") String cep);

}

Pronto, agora tudo esta completo.

Criamos o serviço, criamos o método(requisição), definimos o tipo de verbo, definimos o parâmetro e por último informamos o retorno.

Um detalhe que talvez para você que não leu o primeiro post esteja pensando é em relação a classe CEP, de onde ela vem? Para te ajudar, segue o código dela que também pode ser encontrado no primeiro post:

@JsonIgnoreProperties({"codibge", "codestado"})
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();
    }
}

A única diferença aqui é em relação ao @JsonIgnoreProperties, essa anotação serve para ignorar campos que contém em nossa resposta porém não existe em nosso modelo, com essa tag estamos falando para o Jackson: “Olha Jackson, existe as propriedades codibge e codestado em nossa resposta, porém na hora de realizar a conversão para um objeto, pode ignorá-las. Se essa anotação não existir ele irá tentar pegar o valor de codibge e codestado e tentar atribuir o valor para algum atributo com o mesmo nome, como ambos não existem iria dar uma Exception.

Utilizando o Retrofit

Como o foco do post é na utilização do Retrofit irei disponibilizar o layout assim como foi no primeiro post:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://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_retrofit.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"/>

    </LinearLayout>

    <TextView
        android:layout_height="match_parent"
        android:id="@+id/etMain_resposta"
        android:textColor="#595959"
        android:textSize="20sp"
        android:layout_width="match_parent"/>

</LinearLayout>

Com esse layout você já deve ter uma app parecido com:

Layout da APP

Definindo o click do botão

Novamente, mais código igual, precisamos ouvir o click do botão, assim que o mesmo for clicado a requisição deve ser feita e uma resposta deve ser informada ao usuário, podemos fazer isso da seguinte maneira:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        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) {
                // aqui devemos utilizar o retrofit
            }
        });
    }

}

Para começar a usar nosso serviço, devemos falar para o Retrofit criá-lo, mas como podemos fazer isso? Através do método create:

public CEPService getCEPService() {
	return this.retrofit.create(CEPService.class);
}

Nossa classe de configuração completa fica da seguinte maneira:

public class RetrofitConfig {

    private final Retrofit retrofit;

    public RetrofitConfig() {
        this.retrofit = new Retrofit.Builder()
                .baseUrl("http://ws.matheuscastiglioni.com.br/ws/")
                .addConverterFactory(JacksonConverterFactory.create())
                .build();
    }

    public CEPService getCEPService() {
        return this.retrofit.create(CEPService.class);
    }

}

Vamos então criar e pegar o serviço do CEP criado pelo Retrofit:

new RetrofitConfig().getCEPService().buscarCEP();

Apenas com esse código não irá funcionar, lembra que definimos um parâmetro para o método buscarCEP, pois é, devemos informá-lo, mas de onde iremos pegar o CEP? Se você reparar em nosso código, já temos uma referência para o EditText onde o usuário digita o CEP:

final EditText cep = findViewById(R.id.etMain_cep);

Precisamos apenas pegar seu valor e passar para o método:

new RetrofitConfig().getCEPService().buscarCEP(cep.getText().toString());

Pronto, com isso o Retrofit irá retornar uma Call, esta lembrado? Vamos atribuir essa Call para uma variável:

Call<CEP> call = new RetrofitConfig().getCEPService().buscarCEP(cep.getText().toString());

Porém reparem que ainda não realizamos a requisição, apenas criamos e configuramos, como podemos de fato então realizar o request e obter a resposta(response)? Para isso temos o método enqueue que recebe uma classe anônima do tipo CallBack:

call.enqueue(new Callback<CEP>() {
	@Override
	public void onResponse(Call<CEP> call, Response<CEP> response) {
		// pegar a resposta
	}

	@Override
	public void onFailure(Call<CEP> call, Throwable t) {
		// tratar algum erro
	}
});

Afinal, como podemos pegar a resposta e obter nosso objeto CEP, poemos fazer isso através do body da nossa response:

 @Override
public void onResponse(Call<CEP> call, Response<CEP> response) {
	CEP cep = response.body();
}

Veja que Jackson já fez tudo para a gente, precisamos agora apenas informar a resposta ao nosso usuário:

 @Override
public void onResponse(Call<CEP> call, Response<CEP> response) {
	CEP cep = response.body();
	resposta.setText(cep.toString());
}

Já haviamos também buscado a referência de nosso TextView onde informamos a resposta:

final TextView resposta = findViewById(R.id.etMain_resposta);

O último detalhe é em relação a alguma falha, devemos logar uma mensagem para sabermos qual erro ocorreu:

@Override
public void onFailure(Call<CEP> call, Throwable t) {
	Log.e("CEPService   ", "Erro ao buscar o cep:" + t.getMessage());
}

Caso tenha se perdido em algum passo ou esteja em dúvida, segue a classe completa:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        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) {
                Call<CEP> call = new RetrofitConfig().getCEPService().buscarCEP(cep.getText().toString());
                call.enqueue(new Callback<CEP>() {
                    @Override
                    public void onResponse(Call<CEP> call, Response<CEP> response) {
                        CEP cep = response.body();
                        resposta.setText(cep.toString());
                    }

                    @Override
                    public void onFailure(Call<CEP> call, Throwable t) {
                        Log.e("CEPService   ", "Erro ao buscar o cep:" + t.getMessage());
                    }
                });
            }
        });
    }

}

Pronto, agora já devemos ser capazes de informar e buscar os dados de um CEP de maneira muito mais fácil.

Galinha animada

Não acredita que tudo deu certo? Então veja o funcionamento abaixo:

Exemplo com Retrofit

Caso tenha ficado alguma dúvida não deixe de comentar, porém espero que tudo tenha dado certo \o/.

Se precisar o projeto para exemplo do post pode ser encontrado aqui.