Ahora toca una parte muy importante de el proyecto: parsear un XML. Según lo definido inicialmente, la gracia de toda esta aplicación es que yo recibiré un archivo .xml y a partir de aquí mostraré en el mapa las posiciones. Para ello, vamos a parsear este archivo primero y luego vamos a utilizar los datos para insertar marcadores en nuestro mapa.

Puedes seguir anteriores artículos de esta sección aquí

Parsear un XML

Antes de nada, qué significa «parsear». El parseo es un proceso de análisis y posterior transformación de un bloque en un lenguaje/estructura determinada en otro bloqueo de lenguaje/estructura diferente. Básicamente lo que quiero es que mi programa sepa entender el archivo que le paso, y me detecte lo que son posiciones geográficas, lo que son títulos o el indicador de si está libre o no un parking. Así que, antes de nada, he creado un archivo .xml que alamcena posiciones de parkings:

<?xml version=»1.0″ encoding=»UTF-8″?>

<ParkingPositions>

<parking>

<name>Parking UPC</name>

<lat>41.387179</lat>

<lng>2.1125849</lng>

<free>1</free>

</parking>

<parking>

<name>Parking Casa Adri</name>

<lat>41.3845864</lat>

<lng>2.1392143</lng>

<free>1</free>

</parking>

<parking>

<name>Parking Palau Reial</name>

<lat>41.38575629197152</lat>

<lng>2.1175289154052734</lng>

<free>0</free>

</parking>

<parking>

<name>Parking Liceo Frances</name>

<lat>41.392059</lat>

<lng>2.117139</lng>

<free>0</free>

</parking>

</ParkingPositions>

Este archivo no es nada más que un conjunto de objetos <parking> que contienen un nombre <name>, una latitud <lat>, una longitud <lng> y un indicador de si el parking está libre o no <free>. Ahora toca interpretarlo. En Android se puede parsear un XML de muchas maneras: la que ellos proponen utilizando XMLPullParser, DOM o SAX entre muchos. Personalmente he probado de hacerlo de dos maneras diferentes, con el XMLPullParser y con SAX, y tras muchísimos muchísimos quebraderos de cabeza al final he optado por el SAX. Nota: Muchísimas gracias a Salvador Gómez por su tutorial en el parseo con SAX, que ha sido de mucha utilidad.

Paso 1: La classe de objetos

El objetivo final del parseo es obtener una serie de objetos en el formato que queramos. Como yo quiero obtener parkings con los cuatro atributos mencionados anteriormente, lo primero que hay que hacer es crear la classe ParkingMarker:

 

Paso 2: El Handler

A medida que se parsea un documento se van produciendo eventos que se han de tratar. Estos eventos indican por ejemplo que se ha acabado una etiquetao que has llegado al final del documento entre otros. Los principales eventos son:

  • startDocument(): comienza el documento XML.
  • endDocument(): termina el documento XML.
  • startElement(): comienza una etiqueta XML.
  • endElement(): termina una etiqueta XML.
  • characters(): fragmento de texto.

Así pues en nuestro código vamos a tratar todos ellos

Analicemos con detalle lo que hemos hecho. Primero hemos definido una lista de objetos parkings, un objeto parking y un objeto sbText. Este último sirve para ir almacenando el texto que se recupera del parseador. En el startDocument, tan solo hay que instanciar los dos tipos de objeto y en startElement decimos que si nos encontramos con la etiqueta <parking> significa que empieza un nuevo objeto. La función characters tan solo va acumulando el texto para luego tratarlo. Y la gracia de todo está en el endElement. Aquí definimos qué hay que hacer cuando llegas al final de un elemento. He definido en mi caso las siguientes situaciones:

  • Si estaba en la etiqueta <name>, almacena el contenido en el apartado name del ParkingMarker.
  • Si estaba en una de las etiquetas de <lat> o <lng> que lo almacene en su campo correspondiente
  • Si estaba en el campo <free>, que recoja el valor y si es un «1» entonces asignaremos el valor de TRUE al campo y FALSE si es el caso contrario.
  • He añadido un caso en el que si no viene ni un «1» o un «0» que indique que ha habido error en el parseo.

Paso 3. El Parser

Y finalmente el parseador. Esta clase recibe una URL con el XML y empieza a Parsearlo utilizando el ParkingHandler que hemos creado anteriormente.

Insertando el Parseador al proyecto y añadiendo puntos al mapa

Aquí hay un tema delicado que he aprendido a base de prueba y error. Desde la versión 3 de Android, no se permite a las aplicaciones ejecutar operaciones de larga duración en el hilo principal que puedan bloquear temporalmente la interfaz de usuario. Así pues, hay que crear una llamada asíncrona que lo hará en segundo plano. En resumen, lo que toca hacer ahora es lo siguiente. En el método onCreate añadimos las siguientes líneas:

RetrieveFeed task = new RetrieveFeed();
task.execute("https://dl.dropboxusercontent.com/u/123539/parkingpositions.xml");

Y al final de la MainActivity.java, justo antes de cerrarla creamos una clase que extienda AsyncTask Analicemos también con más detalle lo que pasa aquí. En el primer método doInBackground definimos lo que se realiza en segundo plano. En nuestro caso es crear un nuevo parseador con la URL dada y ejectuarlo. Y en onPostExecute ponemos lo que se ha de hacer a continuación: tratamos la lista y para cada elemento (parking) de ella

  • recuperamos la latitud y la longitud
  • si el parking está libre definimos el color verde y si está ocupado el color rojo
  • finalmente añadimos un marcador en la posición recuperada, con el nombre (también recuperado) por título y el icono de un color u otro según su estado.

Como véis, no es excesivamente complicado pero os puedo asegurar que me estuve horas y horas para conseguir que funcionase. Ahora ya lo tenéis todo bien puesto y funcionando a la perfección.

Podéis ver el código del proyecto en gitHub

Ahora tenemos: Una aplicación que parsea un XML con posiciones y añade marcadores para cada una de ellas en un mapa. En la próxima sesión: Dividir la pantalla en dos, peripecias.