Retrofit

Retrofit es una librería para realizar peticiones a una API HTTP.

Desarrollaremos una app que buscará un término en la API de iTunes y mostrará el contenido encontrado.

https://github.com/gerardfp/retrofit

Crea el proyecto

Itunes Api

Utilizaremos la librería retrofit para realizar llamadas a la API de iTunes y obtener los datos. La librería GSON, realizará el mapeo entre datos JSON y objetos Java.

Para utilizar retrofit se necesitan 3 cosas:

Crearemos estos 3 componentes en el mismo fichero (aunque se podría hacer en ficheros distintos). Crearemos la clase Itunes.java y pondremos estos 3 componentes como elementos static de esta clase:

Crea el fichero Itunes.java:

Itunes.java public class Itunes { }

Clases para el mapeo

La respuesta de la llamada a la API http://itunes.apple.com/search?term=beatles es similar a la siguiente:

{ "results": [ { "artistName": "The Beatles", "trackName": "Let It Be", "artworkUrl100": "https://mzstatic.com/image/39b2caf.jpg" }, { "artistName": "The Beatles", "trackName": "Here Comes the Sun", "artworkUrl100": "https://mzstatic.com/image/6edbf5a8.jpg" } ] }

Solamente se deben tener en cuenta los datos que sean de interés para la app

Se necesitarán estas dos clases para mapear la respuesta en objetos Java:

Itunes.java public class Itunes { //... class Respuesta { List<Contenido> results; } class Contenido { String artistName; String trackName; String artworkUrl100; } }

Aquí tienes más info sobre cómo mapear JSON a JAVA

También puedes usar herramientas automáticas como jsonschema2pojo

Api interface

El siguiente paso es crear una interface que defina las llamadas a la API REST.

Las anotaciones en los métodos (y sus parámetros) de la interfaz indican la petición que se debe realizar a la API

Cada método de la interfaz debe tener una anotación que indique el método HTTP (GET, POST, ...). En la anotación se debe especificar la ruta relativa del recurso.

Definiremos un método buscar() mediante el cual haremos la llamada al recurso search/ de la API. Añadiremos un parámetro al método (String texto) cuyo valor se añadirá a la URL con el nombre term:

Itunes.java public class Itunes { //... public static Api api = new Retrofit.Builder() .baseUrl("https://itunes.apple.com/") .addConverterFactory(GsonConverterFactory.create()) .build() .create(Api.class); public interface Api { @GET("search/") Call<Respuesta> buscar(@Query("term") String texto); } }

Retrofit implementará automáticamente los métodos del objeto api definidos en la interface Api. A cada petición HTTP se la añadirá la baseUrl especificada (https://itunes.apple.com/).

Por ejemplo: cuando se llame al método buscar("nirvana") se hará la petición a https://itunes.apple.com/search/?term=nirvana. El valor del parámetro texto se ha añadido a la URL con el nombre term (que hemos especificado en la anotación @Query).

Retrofit nos devolverá un objeto de clase Respuesta rellenado con los datos obtenidos del JSON que haya devuelto la petición.

ViewModel

En el ViewModel añadiremos un método buscar(String texto) que a su vez haga la llamada a buscar(String texto) de la API. La respuesta de la llamada la guardaremos en un MutableLiveData que será observado por la Vista.

La llamada a la API se debe hacer en segundo plano, para no bloquear el Thread Principal. Retrofit gestiona estas llamadas en segundo plano llamando al método enqueue() y pasándole el Callback para que nos devuelva la respuesta (o el error). Para obtener los datos "en sí" de la respuesta hay que acceder a través del método body().

El ViewModel quedará así:

ItunesViewModel.java public class ItunesViewModel extends AndroidViewModel { MutableLiveData<Itunes.Respuesta> respuestaMutableLiveData = new MutableLiveData<>(); public ItunesViewModel(@NonNull Application application) { super(application); } public void buscar(String texto){ Itunes.api.buscar(texto).enqueue(new Callback<Itunes.Respuesta>() { @Override public void onResponse(@NonNull Call<Itunes.Respuesta> call, @NonNull Response<Itunes.Respuesta> response) { respuestaMutableLiveData.postValue(response.body()); } @Override public void onFailure(@NonNull Call<Itunes.Respuesta> call, @NonNull Throwable t) {} }); } }

El callback onFailure se invocará cuando haya un error de red o cuando no se pueda procesar la respuesta. En esta app no gestionaremos el error, pero estaría bien, al menos, que se notificara al usuario.

ItunesFragment

El ItunesFragment debe dar la orden al ViewModel de que haga la petición a la API, cada vez que cambie el texto introducido por el usuario en el SearchView.

También observará la variable respuestaMutableLiveData, para mostrar los resultados en un RecyclerView.

SearchView

Primero veremos como obtener la Respuesta de la API, según vaya cambiando el SearchView. La mostraremos de momento en el Log, y después implementaremos el RecyclerView.

El primer paso es añadir el <SearchView> al layout

fragment_itunes.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <SearchView android:id="@+id/texto" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>

Implementamos el listener del SearchView que nos pasará el texto introducido cada vez que cambie mediante el callback onQueryTextChange(). En ese momento, damos la orden al ViewModel de que haga la petición a la API.

Por otro lado, observamos la variable respuestaMutableLiveData y mostramos la respuesta en el Logcat.

ItunesFragment.java @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { // ... ItunesViewModel itunesViewModel = new ViewModelProvider(this).get(ItunesViewModel.class); binding.texto.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String s) { return false; } @Override public boolean onQueryTextChange(String s) { itunesViewModel.buscar(s); return false; } }); itunesViewModel.respuestaMutableLiveData.observe(getViewLifecycleOwner(), new Observer<Itunes.Respuesta>() { @Override public void onChanged(Itunes.Respuesta respuesta) { respuesta.results.forEach(contenido -> Log.e("ABCD", contenido.artistName + ", " + contenido.trackName + ", " + contenido.artworkUrl100)); } }); }

Si ejecutamos la app, en el Logcat podremos ver cada Contenido del array results de la respuesta, según vayamos introduciendo texto en el SearchView:

E/ABCD: Nirvana, Radio Friendly Unit Shifter, https://mzstatic.com/image/6edbf5a8.jpg E/ABCD: Nirvana, Pennyroyal Tea, https://mzstatic.com/image/a81e35cd.jpg E/ABCD: Nirvana, Serve the Servants, https://mzstatic.com/image/96ae4cf.jpg E/ABCD: Nirvana, Scentless Apprentice, https://mzstatic.com/image/fe9cb34a.jpg ... ...

RecyclerView

El siguiente paso es mostrar el array result (List<Contenido> result) en un RecyclerView.

Si ejecutas la app, debería verse la lista de contenidos.

Práctica

En esta práctica debes implementar una API, y una app que muestre en un RecyclerView una lista con datos obtenidos de la API.

Para implementar la API se necesita un servidor expuesto a Internet. Implementar una API REST básica puede ser bastante sencillo con frameworks como Spring Boot o Jersey.

En otros lenguajes hay muchísimas opciones como Django, Flask, Laravel, Express.js, ASP.NET Core, Rails, y un largo etcétera.

También hay otras herramientas que automatizan la creación de una API REST como Prisma, PostgREST, sheet.best.

La lista de opciones es casi infita...

Firebase

Para esta práctica utilizaremos Firebase.

  1. Loguéate en la consola de Firebase

  2. Haz click en Crear un proyecto. Introduce el nombre que desees para el proyecto. No es necesario que habilites Google Analytics.

  3. En el menú lateral haz click en Cloud Firestore. Luego en Crear base de datos

    Selecciona el "modo producción". Este modo no permite el acceso de lectura, escritura a la base de datos. A continuación modificarmos las reglas para permitir el acceso de lectura.

    En la Ubicación selecciona eur3.

  4. Una vez creada la base de datos, accede al panel Reglas e introduce las siguientes reglas. Publícalas.

    rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read; allow write: if false; } } }
  5. En el panel de Datos se pueden modificar los datos (añadir, modificar, eliminar). Crearemos una colección de albums de música a modo de ejemplo. En tu práctica la debes hacer de lo que quieras.

    Haz click en + Iniciar colección. En el "ID de colección" introduce Albums

    Ahora añadiremos el primer "Documento" de la colección "Albums". Es decir, el primer álbum.

    El "ID del documento" puedes establecerlo con "ID automático".

    A continuación introduce los campos y los valores del primer álbum:

    Para añadir más albums haz click en + Añadir documento.

  6. Para añadir imágenes utilizaremos el servicio Storage.

    Haz click en Subir archvo y selecciona la imagen.

    Una vez subida, obtén la URL de descarga en el panel lateral derecho:

  7. Vuelve a la base de datos, y añade el campo "portada" al documento (album), el valor de este campo será la URL de la imagen:

Ahora ya puedes acceder a la API REST así:

https://firestore.googleapis.com/v1/projects/retrofit-683b2/databases/(default)/documents/albums

Reemplaza albums por el ID de tu colección.

Reemplaza retrofit-683b2 por el ID de tu proyecto: