Mucho ha pasado desde que escribí un post sobre Retrofit 1.9 (por favor, ni lo busques) y creo que es hora, ahora que he migrado mis programas a Retrofit 2.0, de hacer un resumen de lo más básico de la “nueva” versión de Retrofit para que lo tengas operativo en tu app en 10 minutos.

Primero un resumen de enlaces:

  • Página oficial de Retrofit: aquí
  • Página de github de Retrofit: aquí
  • Por qué tengo que saber qué es REST: aquí

Retrofit es un intermediario: nos permitirá hacer consultas REST desde nuestra app y además, le da un aspecto bonito al retorno de datos para que nos sea más fácil manejarlos.

Pues al tema. Para tener Retrofit funcionando hacen falta 3 patas:

  • un servicio (Service),
  • un objeto Retrofit (antiguo adaptador, ahora Retrofit)
  • y una simple llamada (un Call).

Se pueden poner los 3 en el mismo archivo .java, sin embargo a mi me gusta poner cada uno en su archivo para tener todo bien separadito. Y claro, nos falta a quién consultarle. Para hacerlo sencillo voy a consultarle la hora a www.worldclockapi.com.

Hagamos el proyecto juntos.

Pasos previos

El proyecto está hecho en Android Studio 2.2 (AS 2.2).

En este enlace de github tenéis este mismo código completo de lo que vamos a escribir. Para hacer esto más corto, este post tiene los extractos más importantes.

Para que AS entienda nuestro código Retrofit, hemos de decirle que queremos usarlo y la forma más fácil es a través de gradle. La parte de dependencias os quedará:


compile 'com.squareup.retrofit2:retrofit:2.2.0'
compile 'com.squareup.retrofit2:converter-gson:2.2.0'

La primera línea indica que quiero usar la versión 2.2.0 de Retrofit (creo que ya está la 2.3) y la segunda indica que quiero que todo lo que me responda el servidor de worldclockapi de vuelta, sea traducido por el convertido gson. En Retrofit 1.9 esto no hacía falta porque gson era importado por defecto. Desde la versión 2.0 esto ya no es así. Si queréis usar otro tipo de importados (Jackson, etc.) consultad la página oficial de Retrofit.

Y no os olvidéis, vamos a conectarnos a internet, así que hay que declarar este permiso en nuestro manifest.xml

<uses-permission android:name="android.permission.INTERNET" />

Creamos el servicio

El servicio es como un patrón que indica cómo van a ser nuestras consultas y qué esperamos de respuesta. Para hacer esto tenemos que crear un interface con un formato específico y Retrofit se encargará de convertirla en una consulta de tipo REST sin que nosotros tengamos que hacer nada.

Voy a intentar conseguir la hora estándar oficial del oeste europeo. Ésta es la dirección a la que tengo que consultar: “http://www.worldclockapi.com/api/json/west/now&#8221;.

Hay cuatro consultas REST básicas:

  • GET para leer recursos
  • PUT para editar recursos
  • DELETE para borrar recursos
  • POST para crear recursos

Como se trata de conseguir información, he de hacer una consulta de tipo GET.

Aquí va la interface (recordad, el archivo entero con todos sus imports está en github). Creo un archivo que llamo RetrofitService.java (original eh?):


public interface RetrofitService {
@GET("api/json/west/now")
Call<TimeFromWeb> loadTime();
}

Cuatro detalles:

  • Primero declaro que la consulta será de tipo @GET y en un string digo la url sin la parte principal (sin http://www.worldclockapi.com/)
  • Todas las consultas Retrofit 2.x devuelven un objeto Call
  • En el retorno le digo además cómo debe esperar que sean esos datos que vuelven: de acuerdo a un archivo TimeFromWeb.
  • El método al que voy a llamar se llamará loadTime y no lleva parámetros porque la web worldclockapi no los requiere

¿Qué es TimeFromWeb? Veamos, si ponéis la consulta (http://www.worldclockapi.com/api/json/west/now) en vuestro navegador os saldrá esto:

{
 $id: "1",
 currentDateTime: "2017-06-05T00:30+02:00",
 utcOffset: "02:00:00",
 isDayLightSavingsTime: true,
 dayOfTheWeek: "Monday",
 timeZoneName: "W. Europe Standard Time",
 currentFileTime: 131410962242150660,
 ordinalDate: "2017-156",
 serviceResponse: null
}

un objeto de tipo json con varios campos nos viene de vuelta. Digamos que yo quiero coger la hora: currentDateTime. Pues tengo que decirle a Retrofit que precisamente ese es el campo que me interesa. Cómo? Pues tengo que crear otro archivo TimeFromWeb.java dónde tiene que haber una variable que me cuadre con lo que espero recibir (un string en concreto) para que Retrofit pueda crear ese objeto y guardar ahí el valor de retorno. Quedará algo tan sencillo como esto:


public class TimeFromWeb {

private String currentDateTime;

public String getCurrentDateTime() {
return currentDateTime;
}

public void setCurrentDateTime(String currentDateTime) {
this.currentDateTime = currentDateTime;
}
}

Creamos el adaptador

Para que esto funcione he darle unos detalles al objeto Retrofit a fin de que sepa que quiero que use un convertido Gson para darle formato a los datos json que me va a enviar de vuelta worldclockapi y IMPORTANTE indicar el inicio de la url a la que hay que consultar (recordáis que en el @GET sólo le indicamos la parte final?).

Para tener las cosas bien separaditas porque luego esta clase puede ir creciendo mucho (para proyectos grandes), a mi me gusta crear un archivo RetrofitAdapter.java, que queda así en su forma más básica:


public class RetrofitAdapter {

Retrofit retrofit;

public RetrofitAdapter(){
}

public Retrofit getAdapter(){
retrofit = new Retrofit.Builder()
.baseUrl("http://worldclockapi.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit;
}
}
  • Un constructor vacío (que podríais hacer privado, eso ya depende de vuestro proyecto pero sería interesante hacer esto y emplear un método público estático para inicializar el objeto y decidir si crear un objeto nuevo o devolver uno ya existente, etc. Excelente info aquí)
  • Un método que devuelve el objeto Retrofit que me interesa. Este método crea el objeto a travér de un Builder (que proporcional Retrofit) y le indico dos cosas básicas: la url de inicio y el “traductor” que quiero usar (Gson).

Hacemos la llamada

Vamos a pedir la hora. En nuestro archivo Main tengo que hacer solamente un par de cosillas:

  • Crear el objeto Retrofit que para eso escribí antes el archivo RetrofitAdapter.java:

Retrofit retrofit = new RetrofitAdapter().getAdapter();

  • Tengo que crear el sevicio, que para eso antes escribí el archivo RetrofitService.java:

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

  • Finalmente, ya que tengo el servicio, llamo al método que tiene el servicio, loadTime para que lance la consulta a la web

call = service.loadTime();

Hay dos formas de hacer la llamada: síncrona (un lío, Android no deja bloquear el hilo de ejecución así que tendrías que crear vuestros propios hilos para gestionar la consulta desde ellos) o asíncrona (el modo fácil, Retrofit se encarga de todo). Vamos a por la última. Dispara la consulta!!!


call.enqueue(new Callback<TimeFromWeb>() {
@Override
public void onResponse(Call<TimeFromWeb> call, Response<TimeFromWeb> response) {
System.out.println(response.body().getCurrentDateTime());
}

@Override
public void onFailure(Call<TimeFromWeb> call, Throwable t) {
System.out.println(t.getCause().toString());
}
});

Detalles:

  • enqueue hace que la consulta sea asíncrona
  • el new Callback…. ya os lo pedirá AS automáticamente y siempre hemos de sobreescribir dos métodos:
    • OnResponse: para indicar que hago cuando la consulta es correcta y tengo datos
    • OnFailure: para indicar que hago si hay algún error de tipo Throwable

Y CUIDADO!!!!,  este es un cambio importante respecto a Retrofit 1.9. Antes si la consulta daba por ejemplo, un error HTML 401, Retrofit llamaba a OnError pero ahora ya no. Sólo se llama a OnFailure por un fallo de ejecución pero si hay una respuesta del servidor, sea cual sea el código de estado HTML, se llama a OnResponse así que ahora hemos de comprobar SIEMPRE que la repuesta es como la esperamos antes de ponernos a manipular datos que puede que no estén dentro del objeto Response tal y como esperamos……

Y si todo va bien, http://www.worldclockapi.com os habrá mandado como respuesta a vuestra consulta la hora europea oeste, que se os habrá impreso en la consola de AS tal y como le pedimos. LO HEMOS CONSEGUIDO!!!

salidaDeHora

Último detalle

Ahora desde Retrofit 2.0, las consultas son cancelables. Antes aunque el usuario saliera de tu app la consulta se hacía sólo que la respuesta no se usaba. Ahora se pueden cancelar con una simple llamada a un método así que en nuestro onDestroy:


if (!call.isCanceled())
    call.cancel();

Si queréis que haga un segundo o tercer tuto, por favor, comentad abajo. Podemos avanzar un poco más y hacer consultas de otro tipo (POST por ejemplo) y incluir en las consultas parámetros o manipular los headers del HTML….

Anuncios