Android >> Integrando un RESTful Web Service en Android

integrando-restful-android

Los servicios del tipo REST son los más utilizados en la actualidad a la hora de crear un servicio Web. Si no estás familiarizado con este tipos de servicios puedes echar un vistazo a este artículo para una breve introducción.

Creando la aplicación

Vamos a ver en esta entrada como conectar una aplicación Android con un servicio Web RESTful. Para ello vamos a usar la API REST que nos proporciona My Movie API que nos permite acceder a la información almacenada en la Web IMDb. La aplicación descargará la información de una película desde la Web, para ello realizaremos una petición del tipo GET que nos devolverá a su vez un objeto JSON con dicha información.

El diseño de la aplicación va a ser muy sencillo, ya que únicamente contará con un layout y una actividad:

res / layout / activity_main.xml

<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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Introduce el nombre de la película" />

    <EditText
        android:id="@+id/input_pelicula"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:inputType="text" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawableRight="@android:drawable/ic_menu_search"
        android:onClick="buscarPelicula"
        android:text="Buscar" />

</LinearLayout>

MainActivity.java

package com.amatellanes.pelicularest;

import android.app.Activity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;

public class MainActivity extends Activity {

	private EditText inputPelicula;

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

		inputPelicula = (EditText) findViewById(R.id.input_pelicula);
	}

	public void buscarPelicula(View view) {
		String titulo = inputPelicula.getText().toString();
		if (!TextUtils.isEmpty(titulo)) {

		}
	}
}

Si ejecutamos ahora la aplicaciones únicamente tendremos una entrada de texto y un botón que al ser pulsado almacenará el valor introducido en dicha entrada.

Realizando la petición GET

Para realizar la petición GET y poder acceder a la información devuelta en la respuesta vamos a usar la librería http-request. Para instalar dicha librería en nuestra aplicación podemos incluir la dependencia Maven en nuestro proyecto o descargar el fichero HttpRequest.java y añadirlo a nuestro proyecto.

Una vez incluida la librería, lo primero que deberemos hacer para buscar una película es obtener el nombre de la película introducido por el usuario, y a generar la URL para realizar la petición. Si visitamos la página veremos que se pueden especificar en la URL varios parámetros, pero para este ejemplo únicamente vamos a especificar el nombre, el tipo de la respuesta y el número de registros que queremos obtener:

http://mymovieapi.com/?title=the+dark+knight&type=json&limit=10

Únicamente tendremos que parametrizar el valor especificado en el parámetro title, ya que el parámetro type deberá ser json y limit será 10. Para realizar la petición GET deberemos añadir en nuestra actividad una tarea AsyncTask. Definimos un AsyncTask porque no se debe usar un objeto HttpRequest en el hilo principal de la aplicación:

MainActivity.java

	public static final String TAG = "com.amatellanes.pelicularest";

	private class LoadFilmTask extends AsyncTask<String, Long, String> {
		protected String doInBackground(String... urls) {
			try {
				return HttpRequest.get(urls[0]).accept("application/json")
						.body();
			} catch (HttpRequestException exception) {
				return null;
			}
		}

		protected void onPostExecute(String response) {
			Log.i(TAG, response);
		}
	}

En primer lugar realizamos la petición HTTP en el método doInBackground() usando el método get() que nos proporciona la librería http-request, especificamos además que se nos devuelva el cuerpo de la respuesta con el método body(). La respuesta de la petición será tratada en el método onPostExecute(). Ahora actualizamos el método buscarPelicula() para que se cree un nuevo hilo que realizarán la petición con la URL que creamos a partir del nombre de la película introducido:

MainActivity.java

public void buscarPelicula(View view) {
    String titulo = inputPelicula.getText().toString();
    if (!TextUtils.isEmpty(titulo)) {
        String url = String.format(
                "http://mymovieapi.com/?title=%1$s&type=json&limit=10", titulo);
        new LoadFilmTask().execute(url);
    }
}

Si ejecutamos ahora la aplicación veremos que en nuestro log aparecerá el objeto JSON con la información de la película. Antes de hacer la prueba asegúrate que tu dispositivo tenga conexión a Internet.

Parseando el objeto JSON

Ahora vamos a ver como usar la librería Gson para parsear el objeto JSON que hemos obtenido. Vamos a empezar descargando la librería desde este enlace, además podrás encontrar toda la información y ayuda sobre el uso de esta librería. Descargaremos un fichero .jar que añadiremos a nuestro proyecto.

Vamos a empezar viendo como mostrar el objeto JSON formateado en pantalla, ya que nos resultará útil cuando estemos desarrollando nuestra aplicación. Añadimos un TextView a nuestro layout activity_main.xml y modificamos nuestra actividad principal MainActivity.java:

MainActivity.java

	private class LoadFilmTask extends AsyncTask<String, Long, String> {
		protected String doInBackground(String... urls) {
			try {
				return HttpRequest.get(urls[0]).accept("application/json")
						.body();
			} catch (HttpRequestException exception) {
				return null;
			}
		}

		protected void onPostExecute(String response) {
			Log.i(TAG, response);
			TextView textView = (TextView) findViewById(R.id.texto);
			textView.setText(prettyfyJSON(response));

		}
	}

        private String prettyfyJSON(String json) {
                Gson gson = new GsonBuilder().setPrettyPrinting().create();
                JsonParser parser = new JsonParser();
                JsonElement element = parser.parse(json);
                return gson.toJson(element);
        }

Ahora vamos a ver como parsear el objeto JSON en un objeto Java. Para ello primero deberemos crear una clase Java que incluya los campos del objeto JSON. Usando esta librería no tendremos que especificar todos los atributos del objeto JSON que vamos a parsear. Si echamos un vistazo a la información que obtenemos en cada petición, el objeto JSON que obtenemos consta únicamente de string, números y arrays de string, a excepcion del atributo poster que es un objeto JSON anidado. Por lo tanto deberemos crear dos clase una para la información de la película y otra para la del póster. El nombre de las propiedades de las clases deben coincidir con el nombre de los atributos del objeto JSON que vamos a seleccionar:

Pelicula.java

package com.amatellanes.pelicularest;

public class Pelicula {

	private String title;
	private int year;
	private String[] writers;
	private String[] actors;
	private String plot_simple;
	private Poster poster;

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public int getYear() {
		return year;
	}

	public void setYear(int year) {
		this.year = year;
	}

	public String[] getWriters() {
		return writers;
	}

	public void setWriters(String[] writers) {
		this.writers = writers;
	}

	public String[] getActors() {
		return actors;
	}

	public void setActors(String[] actors) {
		this.actors = actors;
	}

	public String getPlot_simple() {
		return plot_simple;
	}

	public void setPlot_simple(String plot_simple) {
		this.plot_simple = plot_simple;
	}

	public Poster getPoster() {
		return poster;
	}

	public void setPoster(Poster poster) {
		this.poster = poster;
	}

}

Poster.java

package com.amatellanes.pelicularest;

public class Poster {

	private String imdb;
	private String cover;

	public String getImdb() {
		return imdb;
	}

	public void setImdb(String imdb) {
		this.imdb = imdb;
	}

	public String getCover() {
		return cover;
	}

	public void setCover(String cover) {
		this.cover = cover;
	}

}

La librería Gson mapeará automáticamente el objeto Poster incluido en el objeto Pelicula. Si nos fijamos el objeto JSON devuelto es un array donde cada posición se corresponde con un objeto JSON que contiene la información de la película, por lo que el parseo se deberá implementar de la siguiente manera.

MainActivity.java

	private List<Pelicula> getPeliculas(String json) {
		Gson gson = new Gson();
		Type type = new TypeToken<ArrayList<Pelicula>>(){}.getType();
		return gson.fromJson(json, type);
	}

Si quieres ver un ejemplo de un parseo de un objeto JSON simple puedes consultarlo en esta página.

Al método anterior le pasaremos el objeto JSON que obtenemos en la respuesta y éste será convertido una lista de objetos Pelicula. La acción opuesta (de objeto Java a objeto JSON) puedes verlo en la documentación de la librería Gson.

Une vez obtenemos una lista de objetos Java vamos a mostrar como primer ejemplo un único elemento. Modificamos nuestro layout principal donde añadiremos los elementos que queremos mostrar de la película:

res / layout / activity_main.xml

<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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Introduce el nombre de la película" />

    <EditText
        android:id="@+id/input_pelicula"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:inputType="text"
        android:text="The Dark Knight" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawableRight="@android:drawable/ic_menu_search"
        android:onClick="buscarPelicula"
        android:text="Buscar" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <ImageView
            android:id="@+id/ivPoster"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:contentDescription="@string/app_name" />

        <TextView
            android:id="@+id/tvTitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_toRightOf="@id/ivPoster"
            android:singleLine="true" />

        <TextView
            android:id="@+id/tvWritters"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/tvTitle"
            android:layout_toRightOf="@id/ivPoster"
            android:singleLine="true" />

        <TextView
            android:id="@+id/tvActors"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/tvWritters"
            android:layout_toRightOf="@id/ivPoster"
            android:singleLine="true" />

        <TextView
            android:id="@+id/tvPlot"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/tvActors"
            android:layout_toRightOf="@id/ivPoster"
            android:maxLines="2" />
    </RelativeLayout>

</LinearLayout>

Tendremos que modificar el inicio de nuestra actividad para preparar el nuevo layout:

MainActivity.java

	private ImageView ivPoster;
	private TextView tvTitle, tvWritters, tvActors, tvPlot;

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

		prepareUI();
	}

	private void prepareUI() {
		inputPelicula = (EditText) findViewById(R.id.input_pelicula);
		
		ivPoster = (ImageView) findViewById(R.id.ivPoster);
		tvTitle = (TextView) findViewById(R.id.tvTitle);
		tvWritters = (TextView) findViewById(R.id.tvWritters);
		tvActors = (TextView) findViewById(R.id.tvActors);
		tvPlot = (TextView) findViewById(R.id.tvPlot);
	}

Ahora definimos una función que muestre la información de la película por pantalla. Como vemos en el objeto JSON que devuelve la Web y que es parseado a un objeto Poster, obtenemos la URL de la imagen del póster de la película, por lo que deberemos de cargar la imagen en nuestra aplicación a partir de esta dirección. Para ello vamos a hacer uso de la librería Picasso. Puedes incluirla como una dependencia Maven o descargarte el fichero .jar, copiarlo en el directorio libs de tu aplicación y añadirlo al path de la aplicación. El método quedaría de la siguiente manera:

MainActivity.java

	private void mostrarPelicula(Pelicula pelicula) {
		
		tvTitle.setText(pelicula.getTitle());
		tvWritters.setText(Arrays.toString(pelicula.getWriters()));
		tvActors.setText(Arrays.toString(pelicula.getActors()));
		tvPlot.setText(pelicula.getPlot_simple());
		
		if (pelicula.getPoster() != null
				&& pelicula.getPoster().getImdb() != null) {
			Picasso.with(getApplicationContext())
					.load(pelicula.getPoster().getImdb()).into(ivPoster);
		}
	}

Como vemos, a la hora de cargar el póster, comprobamos en primer lugar que existe dicho póster y si es así, cargamos la imagen en la aplicación usando la librería Picasso. Por último, modificamos el método onPostExecute() de nuestro AsyncTask:

MainActivity.java

		protected void onPostExecute(String response) {
			List<Pelicula> peliculas = getPeliculas(response);

			if (!peliculas.isEmpty()) {
				mostrarPelicula(peliculas.get(0));
			}
		}

Si arrancamos la aplicación veremos como se carga la información de la película seleccionada correctamente:

El siguiente paso es construir una lista con la información obtenida en la petición. Para ello podemos usar lo visto en esta entrada donde explico el uso del elemento ListView en Android. Para no alargar más esta entrada os dejo el código en mi GitHub y aquí un vídeo con el resultado:

Descargar código | GitHub