Por que eu devo ler este artigo:

A Retrofit é uma API desenvolvida pela Square seguindo padrão REST, fornecendo um padrão simples de implementação para transmissão de dados entre aplicação e servidor, que faz uso do JSON. Este artigo tem por finalidade a apresentação da API Retrofit de conexão HTTP para Android. O entendimento sobre como a API Retrofit pode ser utilizada é importante porque ela é considerada uma ótima opção quando há necessidade de integração entre sua aplicação e serviços disponibilizados no backend.

Dando continuidade aos artigos sobre APIs para conexão de aplicações Android ao seu serviço/backend, abordaremos as práticas iniciais para cada tecnologia apresentada, facilitando a escolha da melhor implementação para seu caso, considerando: tipos de dados trocados entre a APP e seu backend, facilidade de integração e velocidade na transmissão. As conexões com serviços WEB são um fator decisivo na qualidade que seu usuário perceberá em sua aplicação. Ter uma implementação focada em sua realidade e com tecnologias bem suportadas faz toda diferença.

Ainda hoje é comum encontrar desenvolvedores (geralmente iniciantes) que fazem uso de recursos nativos do Android como o AsyncTask para recuperar dados a partir de servidores. É possível? Sim! É recomendado? Não!

O primeiro fato que precisamos registrar é que o AsyncTask é um recurso que funciona perfeitamente, mas não deve ser utilizado para execução de com longas durações (veja a seção Links). Como posso garantir que recuperar dados a partir de servidores não é uma tarefa longa? Existem inúmeras variáveis envolvidas para conseguirmos uma resposta para essa pergunta e não dá para simplesmente colocar o projeto em risco, concorda?

Além disso, a recuperação de dados utilizando a AsyncTask como recurso pode lhe trazer alguns problemas pelo fato de:

  1. Não existir uma maneira fácil de executar requisições em paralelo. Experimente codificar requisições de dados em paralelo utilizando o AsyncTask em uma aplicação que pode ser executada em versões mais antigas do Android (por exemplo API de Level 7 e 8) em conjunto com as mais atuais. Infelizmente você terá um pouquinho de trabalho para tratar o seu código visto que, quando o AsyncTask foi implementado pela primeira vez, foi construído para ser executado serialmente em uma única background thread;
  2. AsyncTasks podem ser canceladas, mas não lhe provê a possibilidade de cancelamentos de chamadas de rede;
  3. Baixíssimo desempenho de execução para as versões mais antigas por serem executadas serialmente.

Dessa forma, é recomendado que se procure por uma segunda solução.

O objetivo deste artigo é apresentar a API de transmissão de dados Retrofit, mantida pela Square e apresentada em 2013 como uma alternativa simples, rápida e eficiente em desenvolvimento da camada de comunicação de aplicações que utilizem padrão REST (veja a seção Links).

Os serviços WEB provendo dados para seus aplicativos facilita a publicação de novas versões do serviço com correções de bugs, entrega de dados mais precisos, além de uma única implementação para ambas as plataformas. Isso é fornecer software como um serviço aos seus usuários, seja através de WEB Services, Sockets ou qualquer protocolo proprietário.

Para tornar possível tudo isso, a Square incorporou ao Retrofit o GSON como parser padrão para JSON, modelou o mapeamento das conexões utilizando interfaces e anotações e tornou simples a modificação de parâmetros enviados nas requisições em tempo de execução. O simples fato do uso de REST como mecanismo padrão do protocolo que a API implementa torna a curva de aprendizado bem menor e direta.

Entendendo o funcionamento do Retrofit

A API Retrofit é composta pelo uso de outras APIs para garantir aquilo que ela promove de melhor, a conexão e consumo de webservices padrão RESTful. A Square utilizou padrões da API padrão Android/Java para paralelizar as requisições usando Executors, GSON para serialidade das respostas JSON recebidas e OkHttp para tratar de conectar, efetuar cache e fazer chamadas HTTP (veja mais na seção Links), mantida pela mesma Square.

Essa estratégia permitiu que o desenvolvedor tivesse à disponivel implementações e controles abstraídos para gerenciar a conexão, através de anotações nos métodos que assinam as chamadas nas interfaces escritas, ficando ao Retrofit o trabalho de lidar com as chamadas diretas às APIs que ele incorpora. É nesse ponto que mora a simplicidade dessa API da Square: baixo acoplamento e alta coesão. Isso foi levado muito a sério e permitiu o surgimento de um modo extremamente simples para que você tenha em sua mão com pouco código os objetos que sua aplicação precisa e são transmitidos por um protocolo Rest. A seguir explicaremos de forma simples, porém completa como foi modelada essa solução. A Figura 1 nos auxiliará nessa explicação e no parágrafo seguinte trataremos de mostrar o que cada um dos principais componentes da API oferece ao Retrofit.

O OkHttp é desenvolvido e mantido pela própria Square, facilitando e muito que o Retrofit entregue aquilo que há de mais atual em conexões entre dispositivos mobile e seus serviços. Tendo como API mãe a HttpUrlConnection do SDK Android, esse componente, que por si só pode ser usado como API para seu projeto independente do Retrofit, trata de facilitar a chamada de métodos remotos em servidores RESTful. São basicamente três métodos centrais que oferecem de graça: controle de cache, tratamento de chamada e de retorno. Controlar com eficiência o consumo de banda e garantir uma boa velocidade de transmissão com controle de reconexão e cache são primordiais para o sucesso de qualquer aplicação mobile. O OkHttp possui compactação transparente dos dados usando Gzip, pool de conexões para diminuir latência entre chamadas, controle de conexão transparente, gerenciando quedas e permitindo conexões múltiplas ao mesmo serviço através de IPs múltiplos e fazendo ainda controle de cache para respostas em tempo recorde totalmente transparente ao usuário.

Já o GSON é um projeto open source mantido pelo Google e facilita muito a vida de quem trabalha com JSON, seja direta ou indiretamente como no uso feito pela Square. O Google pensou: preciso de uma ferramenta que faça a conversão direta dos JSON que recebo de meus servidores em objetos Java (já mapeados), tudo isso transparente no momento da conversão. Apesar de não utilizado desse modo pela Square, o GSON permite que objetos sejam convertidos diretamente em estruturas JSON para serem transmitidas entre clientes e servidores. Como ele faz isso? Usando toString() e objetos pré-criados (factory-method) que são preenchidos com os dados contidos no JSON e entregues ao usuário e tudo isso com objetos complexos compostos ou associados.

Na escrita de código utilizando Retrofit, o desenvolvimento de interface para chamada dos métodos disponíveis no servidor é feito se descrevendo o tipo de chamada HTTP, se haverá cache, controle de header, etc., através de annotations e assinatura da chamada REST a ser executada e o retorno que será recebido, seja por retorno direto ou call-back, conforme a camada “Sua App” da Figura 1. Durante uma chamada, o Retrofit interpreta cada um desses parâmetros e recebe sua lista de requisições criadas para então interpretar uma a uma e disparar múltiplas threads com uso de Executors, conforme a camada Retrofit API da Figura 1. Todas as conexões, chamadas HTTP, controle de erros de conexão e recuperação de conexão, cache (se configurado) são feitos pelo OkHttp, que entregará ao Retrofit um JSON, isso pode ser observado na seção OkHttp da Figura 1. Nesse momento, conforme o Parser escolhido (por padrão é utilizado o GSON), o JSON será lido e o objeto de resposta definido será preenchido com os dados recebidos, sendo ele único ou uma lista, de forma transparente.

Veja que de fato a simplicidade e flexibilidade de implementação no Retrofit é reflexo de como a API foi desenvolvida. Apesar de poderosa, a API é fortemente recomendada para projetos simples e que atendam a certos critérios, incluindo protocolo com pacotes curtos (poucas informações transmitidas) e seu serviço seguir o padrão REST – que muitos conhecem e que de forma resumida são chamadas HTTP cujos parâmetros são transmitidos em forma de strings representando XML ou JSON e cujas respostas seguem o mesmo padrão. Nossa procura foi por um serviço simples e que atendesse aos requisitos apresentados e permitisse que focássemos no mais importante. Escolhemos uma interface de condições climáticas que permitiu abordar todos os pontos através de uma só função, recebendo o nome da cidade que se deseja obter as condições climáticas totais: temperatura, condições de tempo, velocidade do vento, pressão atmosférica e também dados complementares para aqueles que desejem implementar uma interface rica com ícones indicados no JSON de resposta.

Utilizamos apenas um método para tornar mais simples a leitura, mas a API possui chamadas para obtenção de informações das cidades, conjunto de cidades por raio tendo como bases coordenadas geográficas e diversos tipos de representação de medidas e línguas nas respostas (usamos Português parametrizado para respostas em nossa língua). Em nosso exemplo, constituímos POJOs que associam diretamente a resposta aos devidos atributos de objetos.

Fluxo trabalho Retrofit
Figura 1. Fluxo trabalho Retrofit

Configurando o Retrofit em seu projeto

Com Retrofit é possível criar um Http client robusto de forma muito simples, como demonstraremos nesse artigo. Para demonstrarmos sua praticidade de implementação construiremos um exemplo consumindo uma API de previsão do tempo (veja na seção Links), de modo que a aplicação exemplo faça uma requisição ao serviço que, por sua vez, retornará um JSON com dados referentes à previsão do tempo de uma determinada cidade, informada na requisição. O Retrofit se encarrega de transformar a reposta em um objeto, utilizando o GSON.

Através do site da Square é possível fazer o download (.jar), podendo também obter através do maven ou grable. Também se faz necessário ter a biblioteca do GSON (veja a seção Links).

Após obter os arquivos basta criar o projeto no ambiente de sua escolha. Aqui utilizaremos o eclipse + ADT (veja na seção Links), e consumiremos uma API de previsão de tempo. Uma vez com o projeto criado e de posse das bibliotecas, é necessário adicioná-las na nossa aplicação. Para isso, clique no botão direito do mouse em cima da pasta do projeto e em seguida no menu Build Path, aparecerá um submenu e então clique em Configure Build Path. Na janela aberta clique em add external JARs e assim selecione os arquivos jars do Retrofit e do GSON (Figura 2).

Adicionando os jars das bibliotecas
Figura 2. Adicionando os jars das bibliotecas

Agora, com as bibliotecas no projeto já podemos implementar a chamada para o serviço. Entretanto, para que nosso aplicativo funcione desse modo, é necessário configurar algumas permissões no arquivo AndroidManifest.xml do nosso projeto.

A permissão necessária para que possamos fazer a requisição através da internet você pode ver na Listagem 1, no trecho 01: android.permission.INTERNET.


<?xml version="1.0" encoding="utf-8"?>
  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.devmedia.artigoRetrofit"
      android:versionCode="1"
      android:versionName="1.0" >
   
      <uses-sdk
          android:minSdkVersion="8"
          android:targetSdkVersion="21" />
   
      <!-- Trecho 01 -->
      <uses-permission android:name="android.permission.INTERNET"/>
      
      <application
          android:allowBackup="true"
          android:icon="@drawable/ic_launcher"
          android:label="@string/app_name"
          android:theme="@style/AppTheme" >
          <activity
              android:name=".MainActivity"
              android:label="@string/app_name" >
              <intent-filter>
                  <action android:name="android.intent.action.MAIN" />
                  <category 
                  android:name="android.intent.category.LAUNCHER" />
              </intent-filter>
          </activity>
      </application>
  </manifest>
Listagem 1. Configuração das permissões do projeto no AndroidManifest.xml

Utilizando a API Retrofit

Uma das maiores vantagens de se utilizar o Retrofit é a maneira simples de se implementar. Economizando várias linhas deixando seu código limpo e com o foco apenas nas regras de negócio. As chamadas para o serviço serão feitas com base em uma interface (Listagem 2).

A forma que torna essa simplicidade possível é a utilização de anotações. Na interface, na assinatura do método, é passado uma anotação com o método http que será utilizado na requisição sendo elas @GET(“url”), @POST(“url”), @PUT(“url”) e @DELETE(“url”).

No exemplo representado pela Listagem 2 utilizamos a anotação @GET passando como parâmetro o caminho que compõe a URL do serviço que será acessado (trecho 01). O método que getWeatherByCity() recebe como parâmetro terá o valor que será enviado na requisição. Para isso, é usada uma anotação @Query(“parâmetro”) onde esse parâmetro é o que o serviço está configurado para receber, no nosso exemplo utilizamos o valor “q”. Também recebe como parâmetro uma instância da classe Callback com o tipo do objeto que iremos retornar. O Callback fará com que a chamada seja feita de forma assíncrona, assim é possível fazer o tratamento da response sem que a aplicação fique esperando a resposta do servidor (trecho 02).

Também é possível passar parâmetros no header da requisição, como o tempo de cache com a anotação @Headers (trecho 03).

import Retrofit.Callback;
  import Retrofit.http.GET;
  import Retrofit.http.Query;
   
  import com.devmedia.artigoRetrofit.pojos.City;
   
  public interface OpenWeatherService {
        /*Trecho 01*/
        @GET("/weather?lang=pt")
        /*Trecho 02*/
        void getWeatherByCity(@Query("q") String q, 
  Callback<City> callback);
   
        /*Trecho 03*/
        @Headers("Cache-Control: max-age=640000")
        @GET("/weather?lang=pt")
        void getWeatherByCityWithConverter(@Query("q") String q, 
  Callback<City> callback);
  }
Listagem 2. Construindo a interface

A fim de demonstrar o funcionamento da API, criamos uma tela básica com os botões responsáveis por realizar as chamadas para os métodos que explicitam o funcionamento da biblioteca (Figura 3).

Tela principal da aplicação
Figura 3. Tela principal da aplicação

O layout foi montado com um EditText que o usuário informará o nome da cidade que deseja saber a temperatura (trecho 01 da Listagem 3), botões que executarão a requisição ao serviço para obter a informação, sendo que o primeiro botão utilizará o parser nativo do Retrofit, que é o GSON (trecho 02) e o segundo chamará uma classe que implementamos – que será apresentada mais adiante.

Além disso, criamos um TextView responsável por exibir a temperatura retornada pelo serviço (trecho 03).

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:tools="http://schemas.android.com/tools"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:orientation="vertical"
      tools:context="com.devmedia.artigoRetrofit.MainActivity" >
      <!-- Trecho 01 -->
      <TextView
          android:id="@+id/tvCidade"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:text="Digite uma cidade"
          />
      
      <EditText 
          android:id="@+id/etCidade"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:inputType="text"
          />
      
      <!-- Trecho 02 -->
      <Button 
          android:id="@+id/btnParserGSON"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:text="Parser GSON"
          />
      
       <Button 
          android:id="@+id/btnParserJObject"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:text="Parser JSONObject"
          />
      
       <!-- Trecho 03 -->
      <TextView
          android:id="@+id/tvTemp"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"/>
  </LinearLayout>
Listagem 3. Construindo o layout para nossa aplicação

Como já comentado, o Retrofit utiliza a API GSON para fazer o parser, que é a conversão da String em formato JSON em um objeto Java. O GSON faz isso de forma bem simples. Entretanto, se desejar, você poderá implementar o seu próprio parser utilizando os recursos nativos do próprio Android.

Seguindo com o uso do GSON, criamos uma classe que implementa a interface Converter da biblioteca, e ao realizar esta ação, será necessário sobrescrever dois métodos:

  • fromBody: colocaremos neste método a regra para o parser customizado. Nele recebemos dois parâmetros: TypedInput e um Type.
  • TypedInput contém o retorno da requisição que pode ser acessado através de um InputStreamReader (trecho 02 da Listagem 4). Criamos um BufferedReader recebendo como parâmetro o InputStreamReader com o InputStrem referente à resposta. Depois disso varremos as linhas do BufferedReader e construímos um StringBuilder com o resultado, ficando fácil agora transformar o retorno em um JSONObject (trecho 03). Feito isso, basta criar o objeto da regra de negócio, que no nosso caso é uma “city”, e passar os dados para ele. Feito isso, basta retorná-lo.
  • toBody: esse método é utilizado para transformar o seu objeto em um JSON, contudo, não abordamos no nosso exemplo (trecho 04).

// imports omitidos
   
  public class ExampleConverter implements Converter {
        
    // Trecho 01 
    @Override
    public Object fromBody(TypedInput body, Type type)
      throws ConversionException {

      City city = new City();
      Main main = new Main();

      try {

          // Trecho 02
          BufferedReader reader = 
          new BufferedReader(new InputStreamReader(
                      body.in()));
          StringBuilder out = new StringBuilder();
          String newLine = 
          System.getProperty("line.separator");
          String line;
          while ((line = reader.readLine()) != null) {
              out.append(line);
              out.append(newLine);
          }

          // Trecho 03
          JSONObject JSONCity = 
          new JSONObject(out.toString());

          JSONObject JSONMain = 
          JSONCity.getJSONObject("main");
          
          main.setTemp(JSONMain.optDouble("temp"));
          city.setMain(main);

        } catch (IOException | JSONException e) {

            e.printStackTrace();
        }

        return city;
    }

    // Trecho 04
    @Override
    public TypedOutput toBody(Object arg0) {

      return null;
    }
  }
Listagem 4. Criando o parser customizado

Agora é preciso construir o cliente. Para isso utilizaremos a classe RestAdapter para configurar o acesso ao serviço.

Na sua criação é passada a URL para conexão com o serviço através de um endpoint (trecho 01 da Listagem 5). Para criar o cliente usamos o RestAdapter já configurado, passando a interface OpenWeatherService que criamos nos passos descritos anteriormente (trecho 02).

Enfim disparamos a requisição através do método passando a cidade que queremos pesquisar e uma nova instância da classe Callback que sobrescreve dois métodos:

  • success: que é executado caso a requisição seja completada. Neste método devemos implementar o que fazer com o resultado obtido.
  • failure: caso haja alguma falha requisição (trecho 03) deve-se implementar o tratamento necessário.

// Imports
  public class MainActivity extends ActionBarActivity {
   
    private TextView tvCidade;
    private EditText etCidade;
    private TextView tvTemp;
    private Button btnParserGSON;
    private Button btnParserJObject;

    private RestAdapter restAdapter;

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

      etCidade = (EditText) findViewById(R.id.etCidade);
      tvTemp = (TextView) findViewById(R.id.tvTemp);
      btnParserGSON = (Button) findViewById(R.id.btnParserGSON);
      btnParserJObject = (Button) 
      findViewById(R.id.btnParserJObject);
   
      // Criando o RestAdapter Trecho 01
       restAdapter = 
       new RestAdapter.Builder()
       .setEndpoint("http://api.openweathermap.org" +
       "/data/2.5/").build();
   
      // Implementação da interface Trecho 02
      btnParserGSON.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        OpenWeatherService openWeatherService = 
        restAdapter.create(OpenWeatherService.class);

        // Chamada ao serviço Trecho 03
        openWeatherService
        .getWeatherByCity(etCidade.getText().toString(),
        new Callback<City>() {
          @Override
          public void failure(RetrofitError error) {
             Log.e("Error Retrofit", 
             error.getMessage());
          }
   
          @Override
          public void success(City city, Response resp) {            
          tvTemp.setText(city.getMain()
          .getTemp().toString());
                     }
                });
          }
    });
   
    // Executando a chamada ao serviço usando o parser customizado Trecho 04
    btnParserJObject.setOnClickListener(
    new OnClickListener() {
      @Override
      public void onClick(View v) {
        RestAdapter restAdapterParser = 
        new RestAdapter.Builder()
        .setConverter(new ExampleConverter())
        .setEndpoint(http://api.openweathermap.org +"/data/2.5/").build();             
        OpenWeatherService openWeatherService = restAdapterParser.create(OpenWeatherService.class);  
        openWeatherService.getWeatherByCityWithConverter(
        etCidade.getText().toString(),
        new Callback<City>() {
         @Override
         public void failure(
         RetrofitError error) {
         Log.e("Error Retrofit", error.getMessage());
         }
   
         @Override
         public void success(City city,
         Response resp) {
         tvTemp.setText(city.getMain()
         .getTemp().toString());
         }
         });
        }
    });
  }
}
Listagem 5. Executando as requisições

O método success tem como retorno o JSON já convertido em um objeto da classe City (Listagem 6). Esta e outras classes existentes no projeto são abstrações da estrutura do JSON retornado pelo serviço. Estas classes são utilizadas pelo GSON para serialização do JSON em objeto.

No nosso processo o parser com o GSON acontece de forma padrão no RestAdapter. Para utilizar a classe ExampleConverter que criamos e assim ter um parser customizado basta acrescentar ao RestAdapter na sua criação o método setConverter() passando como parâmetro a nossa classe converter. Observe o trecho 04 da Listagem 5.

Também é possível definir no RestAdapter um Executor que implementa ao paralelismo das requisições. No nosso caso, utilizamos o Callback nas chamadas dos métodos. A implementação foi feita direto na MainActivity, uma vez que nossa lógica é simples. É possível observar o resultado na Figura 4.


import java.util.ArrayList;
  import java.util.List;
   
  public class City {
    private Coord coord;
    private Sys sys;
    private List<Weather> weather = new ArrayList<Weather>();
    private String base;
    private Main main;
    private Wind wind;
    private Clouds clouds;
    private Integer dt;
    private Integer id;
    private String name;
    private Integer cod;

    // GET E SET
  }
   
  public class Clouds {   
    private Integer all;
   
  // GET E SET
  }
   
  public class Coord {    
    private Double lon;
    private Double lat;
   
  // GET E SET
  }
   
  public class Sys {
    private Integer type;
    private Integer id;
    private Double message;
    private String country;
    private Integer sunrise;
    private Integer sunset;
   
  //  GET E SET
  }
   
  public class Weather {  
    private Integer id;
    private String main;
    private String description;
   
  // GET E SET
  }
   
  public class Wind {
    private Double speed;
    private Double deg;
   
  // GET E SET
   
  }
Listagem 6. Classes POJO – baseadas no retorno do serviço on-line
Funcionamento do aplicativo de teste
Figura 4. Funcionamento do aplicativo de teste

A arquitetura e o uso extenso de padrões de projeto tornaram o Retrofit poderoso, simples e ainda é possível sua personalização através de controles diretos no OkHttp e o modo como sua resposta será tratada. A seguir resumimos o que é necessário para que sua solução possa fazer uso correto do Retrofit. São basicamente três as questões no uso e escolha desse framework para comunicação em seu aplicativo:

  1. Seu serviço é RESTful?
  2. Seu projeto é simples e precisa de acessos em mudanças de tela constantes?
  3. Sua aplicação recebe pacotes leves (tamanho reduzido)?

Se a resposta for sim principalmente para as duas primeiras perguntas, o Retrofit pode ser a escolha ideal, além de prover uma integração simples, seu desempenho é unânime em benchmarks feitos pela comunidade. Mas tenha cuidado. Da mesma forma que não indicamos o Volley para conexões que trabalham com grandes massas de dados, streaming e comunicações persistentes, a API da Square em sua simplicidade demonstra sua força e fraqueza, ela estende essa limitação, consumindo apenas serviços REST e não possui métodos específicos para imagens, exigindo tratamento da String hexa para imagens e fugindo do propósito de pacotes leves.

Confira também