NOTA: Este post ha sido actualizado para emplear la nueva versión de Retrofit, Retrofit 2, en el siguiente enlace.

En un app en la que estoy trabajando tengo que vérmelas con las consultas web, en mi caso, para descargar datos en formato json (aunque lo mismo sería aplicable a otros formatos como xml), mediante consultas REST.

Tenemos básicamente dos métodos para desarrollar estas consultas:

  1. Desarrollar nosotros mismos la consulta, para lo que tendremos que programar una simple conexión a internet que descargue los datos y un parseador json para que nos transforme el resultado en objetos que necesitemos manejar. Hay muy buenos tutoriales sobre esto en internet, podéis ver ejemplos aquíaquí o aquí (o excelentes vídeos como el de Xamarín aquí) y por tanto no voy a desarrollar más este tema.
  2. Usar alguna de las múltiples librerías que se encargan de todo este proceso por nosotros realizando, además, una gestión completa de errores de retorno, conexión y, en varios casos, permitiéndonos realizar las solicitudes de modo síncrono o asíncrono con simplemente variar un parámetro (no más preocuparse de Loaders o AsyncTasks !!).

Y sobre esto os voy a hablar. Buscando buscando, he preseleccionado dos: la Android Asynchronous Http Client de James Smith y Retrofit, de los chicos de square. Finalmente, por facilidad, flexibilidad, documentación y ayuda de los usuarios me quedo con Retrofit. Y aquí (por fín) os cuento un resumen de cómo empezar a usarla ya que no hay mucha documentación en español sobre ello.

CÓMO USAR RETROFIT

Retrofit es un cliente REST para Android y Java que se basa en los interfaces Java para facilitarnos su programación. Nosotros creamos un interfaz del tipo de consulta que queremos realizar y la librería retrofit se encarga del resto… más o menos.

  • Creamos un cliente Retrofit en cualquier clase o, mejor, en una específica. Ésta es la última que he usado yo:

public class RetrofitRestClient {

    public static RetrofitRestClient retrofitRestClient;
    private final String URL_BASE = Server.URL_BASE;

    private RetrofitRestClient(){
        Gson gson = new GsonBuilder()
                .setDateFormat("yyyy-MM-dd HH:mm:ss")
                .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
                .disableHtmlEscaping()
                .create();

        RestAdapter adapter = new RestAdapter.Builder()
                .setClient(new OkClient(getHttpClient()))
                .setLogLevel(RestAdapter.LogLevel.FULL)
                .setConverter(new GsonConverter(gson))
                .setEndpoint(URL_BASE)
                .build();

        miServicio = adapter.create(ClaseDeLasInterfaces.class);
    }

    public static RetrofitRestClient getClient(){
        if (retrofitRestClient == null)
            retrofitRestClient = new RetrofitRestClient();
        return retrofitRestClient;
    }

    private OkHttpClient getHttpClient(){
        OkHttpClient httpClient = new OkHttpClient();
        httpClient.setConnectTimeout(60, TimeUnit.SECONDS);
        httpClient.setReadTimeout(60, TimeUnit.SECONDS);
        return httpClient;
    }
}

Uso un patrón singleton para asegurarme que sólo tengo una instancia del cliente Retrofit en mi app. Uso GSON para parsear el resultado de la consulta, que será devuelta por el servidor en formato JSON (hay varios tutos de GSON por la web, si también os interesa publicaré uno). Finalmente, en lugar del cliente por defecto de Android, prefiero usar OkHttpCliente para mis consultas web, dónde lo único que hago es aumentarle el tiempo por defecto que espera la respuesta del servidor para no tener una excepción Java de tipo socketTimeOutException cuando el servidor tarda más de lo habitual en responder. Como véis para crear el adaptador del cliente necesitamos enviarle como parámetro el nombre de la clase dónde tenemos las interfaces en las que declaramos el tipo de consulta que queremos hacer (GET, POST, etc.). Además, la url base de la consulta, también se pasa como parámetro en el Builder en setEndPoint(String url).

  • Creamos un interfaz (o varios) dónde codificamos las consultas REST que queremos realizar, por ejemplo:
public interface GitHubService {
  @GET("/users/{user}/repos")
  Musica listaMusica(@Path("user") String user);
}

se trata de una consulta tipo GET, dónde se indica en un String la url a la que se realiza la consulta: GET (String url), que acepta un parámetro, en este caso el usuario, que se le pasa cuando se realiza la llamada al método. En la url de GET, se pueden especificar parámetros html de consulta como “sort=desc” entre otros.

También es posible indicar parámetros en la propia declaración del método, mediante Query o QueryMap (pares nombre, valor) para parámetros más complejos.

public interface GitHubService {
  @GET("/users/{user}/repos")
  Musica listaMusica(@Path("user") String user,
          @Query ("sort") String sort,
          @QueryMap Map<String, String> opciones);
}
  • La llamada al método GET la realizo dentro de la clase propia dónde me interesa programar la consulta:
Musica miMusica = new Musica();
String usuario = "perico";
String sort = "desc";
Map<String, String> opciones = new HashMap<String, String>();
opciones.put("Title", "MainWindow");
opciones.put("action", "raw");

//Llamada simple
miMusica = miServicio.listaMusica(usuario);

//Llamada con parámetros
miMusica = miServicio.listaMusica(usuario, sort, opciones);

Esto lanzará una consulta de tipo síncrono al servidor que nos devolverá un objeto Musica que será parseado, antes de guardarse en mi objeto miMusica, con el parseador GSON que yo haya indicado al crear el adaptador (ver punto 1).

Si queremos que nuestra consulta sea asíncrona, debemos declarar el método void e incluir en la declaración del GET un Callback que será llamado por retrofit cuando retorne del trabajo online:

public interface GitHubService {
  @GET("/users/{user}/repos")
  void listaMusica(@Path("user") String user,
         Callback <Musica> miMusica);
}

Ahora nuestro trabajo será declarar este Callback:

private Callback miCallback(){
        final Callback<Musica> callback = new Callback<Musica>(){
            // cuando todo vaya bien
            @Override
            public void success(Musica miMusica, Response response){
                // Lo que haya que hacer
            }
            // cuando vaya mal
            @Override
            public void failure(RetrofitError error){
                // aquí podríamos ir haciendo if según los tipos de errores devueltos
                if (error != null) {
                    // Nuestra reacción al error
                }
            }
        };
        return callback;
    }

y cuando llamemos a nuestro servicio para descargar música de modo asíncrono:

miMusica = miServicio.listaMusica(usuario, new miCallback());

Y hasta aquí por hoy.

Más adelante completaremos el post indicando cómo realizar consultas POST, PUT o cómo incluir datos en nuestros encabezados.

Recordaros que toda la información y documentación está disponible en square.github.io/retrofit

Anuncios