Usar Retrofit2 per consumir serveis REST

Retrofit és una llibreria de Java que es fa servir per consumir serveis REST de qualsevol tipus: JSON, XML, Protobuff, etc…

La idea de Retrofit és que no cal que ens preocupem de tot el que fa referència a les peticions per la xarxa, ni de construir els objectes, .. Només cal cridar els mètodes correctes i ell se n’encarregarà de la resta.

A més se n’encarregarà de convertir les dades rebudes en objectes Java. Per exemple si ens arriba un objecte com aquest:

{"id":71,"nom":"Vermell","rgb":"#FF0000"}

Es pot mapejar en un objecte com aquest (he tret els getters i setters per fer la classe més senzilla)

class Color {
    int id;
    String nom;
    String rgb;
}

Exemple d’ús

Volem consumir un servei REST que està a [http://colors-rgb.herokuapp.com]("http://colors-rgb.herokuapp.com") i que proporciona dos mètodes a través de GET:

mètode resultat
/color/nom Retorna les dades del color especificat a nom en format JSON. Per exemple la URL “/color/verd” donarà les dades del color verd.
/colors Llista tots els colors del servei

Afegir les llibreries

Per poder funcionar caldrà tenir la llibreria Retrofit2 en el CLASSPATH. Es pot fer descarregant les llibreries o bé a través d’un gestor de projectes.

Per mi, la forma més senzilla és definir-la en un gestor de projectes Maven o Gradle. En Maven fem una cosa com aquesta:

<dependency>
    <groupId>com.squareup.retrofit2</groupId>
    <artifactId>retrofit</artifactId>
    <version>2.3.0</version>
</dependency>

I en Gradle:

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

Si el que consumim no és HTML també farà falta afegir quin conversor es vol fer servir. En Retrofit hi ha diferents conversors predefinits:

També se’n poden crear de personalitzats (però és una mica més complexe)

Per tant podem fer servir Gson o Jackson per consumir JSON. En el meu cas trio Jackson:

<dependency>
   <groupId>com.squareup.retrofit2</groupId>
   <artifactId>converter-jackson</artifactId>
   <version>2.3.0</version>
</dependency>

Crear els models

En aquest cas el model de dades bàsic és senzill. Només cal crear un objecte Color (Jackson necessita getters i setters):

public class Color {
   int id;
   String nom;
   String rgb;

   public int getId() {
     return id;
   }

   public void setId(int id) {
     this.id = id;
   }

   public String getNom() {
     return nom;
   }

   public void setNom(String nom) {
     this.nom = nom;
   }

   public String getRgb() {
     return rgb;
   }

   public void setRgb(String rgb) {
     this.rgb = rgb;
   }

En general el model es crea seguint les normes del conversor que es farà servir.

Si es fa servir Jackson com a conversor al definir el model es poden fer servir qualsevol de les anotacions de Jackson:

@JsonIgnoreProperties({"nomEnCastella"})
@JsonIgnoreProperties(ignoreUnknown=true)
public class Color {
	public int id;
    public String nom;
	public String nomEnCastella;
	public String rgb;
}

Definir el servei

Per consumir el servei cal definir en una interfície quins són els mètodes de l’API que es faran servir:

import net.xaviersala.model.Color;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;

public interface ColorsRestService {

	@GET("color/{color}")
	Call<Color> getColor(@Path("color") String color);

  @Get("color") Call<Color> getColor(@Query("nom") String nom))

	@GET("/colors")
	Call<List<Color>> getColors();

	@Post("/color/crear")
	Call<Color> crearColor(@Body Color nou);
}

Com es pot veure, amb Retrofit 2 sempre es retorna un objecte parametritzat en Call<T>. Bàsicament això és així per poder cridar els mètodes tant de forma síncrona com asíncrona.

Es defineixen amb anotacions els diferents mètodes HTTP: (GET, POST, DELETE, …), els paràmetres variables @Path, els paràmetres amb @Query, el cos del missatge amb @Body, a més d’altres anotacions sobre com funciona la petició @FormUrlEncoded, @Multipart, @Headers, Field, etc..

Aquí hi poso uns exemples diversos que no tenen a veure amb l’exemple

@FormUrlEncoded
@POST("/afegir/color")
Call<Color> formulariColors(@Field("nom") String nom);

Usar-lo

El programa principal ha de construir un objecte Retrofit, en el que se li pot definir la URL, el conversor que es farà servir, … :

Retrofit retrofit = new Retrofit.Builder()
  .baseUrl("http://colors-rgb.herokuapp.com")
  .addConverterFactory(JacksonConverterFactory.create())
  .build();

A partir de l’objecte Retrofit es crea el servei a partir de la interfície que hem definit abans:

ColorsRestService service = retrofit.create(ColorsRestService.class);

Crida síncrona

Es poden fer crides a l’API de forma síncrona o asíncrona. El mètode síncron és el més senzill. Les crides síncrones es fan amb el mètode execute().

Call<Color> crida =  service.getColor("vermell");
Color color = response.execute().body();

També es pot recollir la resposta en un objecte Response<T> de manera que es podran comprovar altres coses sobre la resposta:

Response<Color> resposta = service.getColor("vermell").execute();
if (response.code == 200) {
    Color color = response.body();
}

Crida asíncrona

Per fer una petició asíncrona caldrà definir els mètodes onResponse() i onFailure() en un Callback i fer servir enqueue() en comptes d’execute() per fer la crida:

Call<Color> call = apiService.getColor("vermell");
call.enqueue(new Callback<Color>() {
    @Override
    public void onResponse(Call<Color> call, Response<Color> response) {
        int statusCode = response.code();
        Color user = response.body();
    }

    @Override
    public void onFailure(Call<Color> call, Throwable t) {
        // error!
    }
});

Projecte GitHub

He fet un projecte a GitHub que fa servir Retrofit2 i que és una mica més elaborat del que he explicat aquí: Exemple

Relacionats: