Android

2 ago, 2016

Retrofit 2: o que devemos saber

Publicidade

Retrofit é uma das mais poderosas e populares bibliotecas de HTTP Client para Android e Java, produzida pela Square Inc. e lançada como open source para toda comunidade.

Um dos princípios do Retrofit é a simplicidade, que permite que não nos preocupemos com toda a complexidade de criar uma conexão Web Service, uma vez que grande parte de sua correspondente lógica é abstraída. Com toda essa proposta, devemos apenas implementar algumas interfaces, anotar alguns métodos e voilà!

1

Neste artigo, vou mostrar um guia sobre a nova atualização desta incrível lib. Vou usar exemplos baseados em um projetinho que fiz consumindo a API do Dribbble – se interessar, está disponível no meu Github.

1. Novo conceito de URL

Na versão 1.x do Retrofit, caso quiséssemos consumir rotas de uma determinada API, bastava setarmos um endpoint e sua correspondente rota que eles seriam concatenados em um path URL final. Olha só como era:

@GET("/shots")
List<ShotsVO> getShotsList(@Query("access_token") String accessToken);

RestAdapter restAdapter = new RestAdapter.Builder()
        .setEndpoint("https://api.dribbble.com/v1")
        .build();

WebServiceApi webServiceApi = restAdapter.create(WebServiceApi.class);
//Resultado final: https://api.dribbble.com/v1/shots

Agora na versão 2.x:

@GET("/shots")
Call<List<ShotsVO>>getShotsList(@Query("access_token") String accessToken);

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://api.dribbble.com/v1")
        .build();

WebServiceApi webServiceApi = retrofit.create(WebServiceApi.class);
//Resultado final: https://api.dribbble.com/shots

Percebeu a diferença?

4-1

Na versão 1.x, conforme mencionado anteriormente, o endpoint é concatenado diretamente com sua rota parcial. Já na versão 2.x, deve-se prestar muita atenção, pois a“Base Url” não é concatenada com sua rota parcial. Você pode perceber que o “v1” será sobrescrito por “/” no início da rota parcial (“/shots”). Isto acontece porque a nova versão do Retrofit usa o método HttpUrl.resolve() para criação da URL final. Este método gera um link similar ao modelo <a href>. Vamos ver como deveríamos consumir uma API, respeitando o novo conceito:

@GET("http://api.icndb.com/jokes/random")
Call<JokeVO> getAJoke();
//Inserindo URL diretamente pelo campo da anotação de HTTP verb

 

@GET
Call<JokeVO> getAJoke(@Url String url);
//Inserindo anotação @Url dentro do método

2. OkHttp

Nas versões 1.x do Retrofit, podíamos setar qualquer HTTP Client que quiséssemos. No Retrofit 2, porém, o OkHttp já vem incluso em sua própria dependência. Isso porque é o OkHttp quem fornece a classe Call (falaremos um pouco mais sobre isso), e por isso sua inclusão é necessária.

2.1. Interceptors

Assim como no Retrofit 1, na versão 2.x, caso queiramos customizar nossas requisições, como adicionar um header ou um log, por exemplo, precisamos sobrescrever o OkHttpClient e adicionar um Interceptor. Após toda a configuração, devemos passar nosso HTTP Client customizado ao método “.client()” do Retrofit, que substituirá o default. Vejamos um exemplo:

OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor(){
    @Override
    public Response intercept(Chain chain) throws IOException{
    ...
}
}
 
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl(Environment.SERVER_URL)
    .client(httpClient.build())
.build();

Como disse anteriormente, caso quiséssemos adicionar um log em nossas requisições, bastava adicionar um interceptor. Pois bem, e se você soubesse da triste notícia de que não há mais logging?

7-432x432

E agora?? =(

O time de desenvolvimento do Retrofit realmente removeu esta feature. Porém, desenvolvedores do OkHttp lançaram separadamente um tipo Http logging, que pode ser adicionado ao seu projeto de uma maneira bem simples: basta adicionar sua dependência e um novo interceptor. Fica assim:

compile 'com.squareup.okhttp3:logging-interceptor:3.2.0'

 

HttpLogginInterceptor interceptor = new HttpLogginInterceptor();
interceptor.setLevel(Environment.LOG_LEVEL);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(interceptor);

Obs: é recomendado adicionar logging como último interceptor, pois ele pode “loggar” suas informações de interceptors de requests anteriores.

11

3. Converters

Nas versões anteriores ao Retrofit 2, um Json Converter vinha incluso como padrão, o Gson. Agora, ele não vem mais integrado por padrão, deve-se definir como uma dependência à parte.

compile 'com.github.aurae.retrofit2:converter-logansquare:1.4.0'

No exemplo, eu usei um converter para a biblioteca LoganSquare, mas existem outros listados no próprio site do RetrofitVocê também pode criar seu próprio converter, basta herdar a classe “Converter.Factory”. Após incluir a dependência, basta adicionar na construção da instância do Retrofit.

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(Environment.SERVER_URL)
        .addConverterFactory(LoganSquareConverterFactory.create())
        .build();

4. Requests

Conhecendo um pouco sobre a versão 1.x, sabemos que para definir um método de característica síncrona, deveríamos criar um método com tipo de retorno. Em contrapartida, um método de característica assíncrona era void e recebia como último parâmetro um CallBack para a resposta do corpo da requisição.

Agora, não existe uma distinção de métodos para uma requisição síncrona ou assíncrona.

Lembra que mencionei no começo sobre o porquê de o OkHttp já vir incluso no Retrofit? Então, precisamos ter esta outra biblioteca adicionada porque ela trabalha com a classeCall, que simplifica a declaração do tipo de resposta das requisições. Basta adicionar “call.execute” para um request síncrono e “call.enqueue(…)” para um request assíncrono. Exemplo de um request síncrono:

Call<List<ShotsVO>> call = apiInstance.getListShots();
List<ShotsVO> shotsList = call.execute.body;

Exemplo de uma requisição assíncrona:

Call<List<ShotsVO>> call = apiInstance.getShotsList();
call.enqueue(new CallBack<List<ShotsVO>>(){
@Override
public void onResponse(Call<List<ShotsVO>> call,          Response<List<ShotsVO>> response){
…
}
...

Não podemos deixar de lembrar que o método “onResponse” não é chamado apenas quando vier uma resposta de sucesso do servidor. Em contrapartida, caso dispare em uma requisição assíncrona um “IOException”, será chamado no “onFailure”, indicando que houve um erro de rede, tal como socket timeout, uknown host etc.

Portanto, é preciso tomar cuidado ao tratar as requisições de sua aplicação, pois comportamentos inesperados poderão aparecer caso não haja essa precaução de tratamento. Para que a classe CallBack satisfaça o resultado esperado para tratamentos eventuais de erro, uma dica seria criar uma classe customizada, que estenderia a classeCallBack ao invés de retornar apenas “onResponse(…)” e “onFailure(…)”. Assim, você poderia tratar conforme o seu desejo.

4.1. Cancelar Requests

Vamos imaginar que queremos fazer uma requisição que, por algum motivo, demore para ser finalizada e, cansado, o usuário não quer esperar o término do request. Ele quer cancelar. Como faríamos?

Calma… No Retrofit 2, isso pode ser feito de uma forma muito simples e objetiva. Basta usar “call.cancel()”. Ficaria assim:

Call<List<ShotsVO>> call = apiInstance.getShotsList();
call.enqueue(new CallBack<List<ShotsVO>>(){
    @Override
    public void onResponse(Call<List<ShotsVO>> call, Response<List<ShotsVO>> response){
    …
}
 
@Override
public void onFailure(Call<List<ShotsVO>> call, Throwable throwable){
…
}
}
//call.cancel();

Devemos prestar atenção em um ponto: após o cancelamento da requisição, o Retrofit vai classificar essa operação como uma falha de rede, se encaixando no parâmetro de tratamento do “onFailure”.

Como falamos anteriormente, o “onFailure” também recebe erros de rede, logo, é preciso diferenciar se houve de fato o disparo do cancelamento da requisição ou um erro de conexão de Internet.

Para salvar nossas vidas (mais uma vez), podemos fazer essa verificação apenas usando o que a classe Call nos oferece, um booleano “call.isCancelled()”.

Call<List<ShotsVO>> call = apiInstance.getShotsList();
call.enqueue(new CallBack<List<ShotsVO>>(){
    @Override
    public void onFailure(Call<List<ShotsVO>> call, Throwable throwable){
        if(call.isCanceled()){
            //requisição foi cancelada
}
else{
    //provavelmente houve um erro de conexão
}
}
}

5. Conclusão

Abordamos aqui algumas comparações, dicas e principais mudanças que são essenciais para termos em mente, mas existem outras que você pode conferir melhor no Change Log do Retrofit, principalmente se gosta de acompanhar com mais detalhes cada lançamento feito.

Espero ter ajudado! Caso tenha alguma dúvida ou algum ponto a acrescentar no assunto abordado, sinta-se à vontade para deixar seu comentário logo abaixo.

Até a próxima!