martes, 10 de enero de 2017

Firebase para Android: Base de Datos en Tiempo Real (5)

Firebase para Android: Base de Datos en Tiempo Real (5)

Curso Programación Android
Este artículo forma parte del Curso de Programación Android que tienes disponible de forma completamente gratuita en sgoliver.net
En los artículos anteriores de esta serie nos hemos centrado principalmente en los distintos mecanismos que nos ofrece Firebase para leer o consultar la información almacenada en la base de datos desde nuestras aplicaciones Android. Aprendimos a leer datos individuales y listas de elementos, vimos cómo suscribirnos a una determina ruta de la base de datos para ser notificados cuando haya cambios en la información, aprendimos a utilizar la librería FirebaseUI para vincular controles Android de tipo lista (ListView/RecyclerView) a la base de datos, y descubrimos cómo filtrar y ordenar la información recuperada.
Sin embargo no solo de leer vive el hombre, y habitualmente también nos será necesario escribir o actualizar información en la base de datos para ponerla a buen recaudo y/o hacerla disponible a otros clientes implicados (recordemos que con Firebase, cada vez que escribamos algo en la base de datos, dicha información se sincronizará automáticamente y al instante en todos los clientes suscritos). Éste será el tema central de este nuevo artículo de la serie.
Empezaremos por la operación más básica, la de escribir un dato sencillo (clave + valor simple) a nuestra base de datos. Para hacer esto, simplemente tendremos que construir una referencia, como siempre de tipo DatabaseReference, a la clave que queremos escribir (ya existente o no) y utilizar sobre ella el método setValue(valor) para establecer su valor. Así, siguiendo con el ejemplo de los días de la semana utilizado en artículos anteriores, si quisiéramos añadir un nuevo elemento a nuestra lista de días de la semana podríamos hacer lo siguiente:
Como podéis comprobar, la referencia sobre la que vamos a escribir podemos obtenerla directamente al crear el objeto DatabaseReference, o bien “navegando” con el método child() sobre una referencia obtenida previamente.
Hay que tener muy en cuenta que con setValue() podemos tanto insertar nuevos elementos como actualizar los ya existentes. Así, si la clave sobre la que escribimos no existe estaremos insertando un nuevo nodo, y si ya existía estaremos sobrescribiendo su valor (y ojo, esto incluye la eliminación de todos sus elementos descendientes si los tenía).
Los posibles valores que acepta el método setValue() son los siguientes:
  • String
  • Long
  • Double
  • Boolean
  • Map<String, Object>
  • List<Object>
  • Objeto Java personalizado
Los cuatro primeros no necesitan mayor explicación, son tipos sencillos, por lo que nos detendremos tan solo en los tres siguientes.
El método setValue() puede recibir como parámetro un objeto de tipo Mapque contenga una serie de pares clave-valor que se insertarán como hijos de la clave sobre la que se está escribiendo. Esto nos puede facilitar la vida a la hora de escribir estructuras de árbol complejas, ya que nos evita tener que escribir nodo a nodo. Así, imaginemos por ejemplo que quisiéramos insertar en nuestra lista de días de la semana un nodo de este tipo:
  • “dia7”:
    • “periodo-1”: “domingo-mañana”
    • “periodo-2”: “domingo-tarde”
    • “periodo-3”: “domingo-noche”
Podríamos crear los tres nodos hijo previamente en forma de objeto Map e insertarlos de una sola vez utilizando el método setValue().
Si consultamos el nuevo nodo creado en la consola de Firebase veremos que efectivamente el resultado ha sido el esperado:
escribir-map
El siguiente tipo admitido es una lista de objetos de tipo List. Si pasamos a setValue() una lista de objetos de este tipo, Firebase creará por nosotros una lista de elementos donde cada uno de ellos tendrá como clave su posición en la lista y como valor el dato de la lista situado en dicha posición. Veamos un ejemplo:
Consultando esta información desde la consola de Firebase veremos nuestra nueva lista en el formato indicado:
escribir-list
La última opción disponible es utilizar objetos Java personalizados. En un artículo pasado vimos como Firebase nos permite recuperar información de la base de datos y mapearla directamente sobre un objeto Java propio que tenga la misma estructura. Pues bien, a la hora de escribir información sobre la base de datos vamos a disponer de las mismas facilidades. El único requisito será como siempre que nuestra clase Java personalizada tenga un constructor por defecto y disponga de getters y setters públicos para los datos que queremos escribir a la base de datos. Siguiendo con el ejemplo de las predicciones meteorológicas, recordemos que ya construimos la siguiente clase para encapsular nuestros datos:
Firebase nos va a permitir construir un objeto de este tipo y pasarlo directamente al método setValue() para utilizarlo como valor de la clave a escribir.
Podemos comprobar en la consola de Firebase que el nodo insertado es el que esperábamos.
escribir-objeto
El método setValue() es la forma más sencilla de insertar o actualizar información de la base de datos, pero nos permite actualizar sólo una clave por operación. Firebase nos ofrece un método alternativo que nos permite actualizar varias claves al mismo tiempo, y además de forma atómica, es decir, que nos garantizará que se realizan correctamente todas las actualizaciones o bien no se realizará ninguna. Este método se llama updateChildren() y recibe como parámetro un objeto de tipo Map con una serie de pares clave-valor donde cada clave es la ruta a una de las referencias que queremos actualizar y el valor será el valor (valga la redundancia) que queremos asignar a dicha referencia.
Así, si por ejemplo queremos actualizar de forma simultánea la temperatura y humedad de una predicción de nuestra lista, podríamos hacer lo siguiente:
Del ejemplo anterior hacer notar que las diferentes rutas indicadas deben comenzar por el caracter “/”. También importante conocer que las rutas incluidas no tienen por qué pertenecer al mismo nodo, sino que pueden indicarse claves de diferentes nodos de nuestra base de datos. Por ejemplo podríamos actualizar de forma simultánea la temperatura de un día y la humedad de otro distinto:
Tanto el método setValue() como updateChildren() pueden recibir de forma opcional un segundo parámetro con un listener de tipo CompletionListener, cuyo método onComplete() se llamará automáticamente cuando la operación se dé por finalizada (correctamente o con algún error). Este método recibe como primer parámetro un objeto de tipo DatabaseError que contendrá el mensaje de error en caso de que la operación no haya finalizado correctamente, o será null en caso de haberse finalizado sin errores. Veamos un ejemplo:
Vamos ahora como otro tema importante que habíamos dejado pendiente en artículos anteriores. Hasta ahora, cada vez que hemos creado o insertado nuevos datos en una lista de elementos en nuestra base de datos Firebase hemos utilizado claves arbitrarias elegidas o construidas por nosotros, por ejemplo: “dia1”, “dia2”, … en nuestro ejemplo de los días de la semana, o “20161122”, “20161123”, … en el ejemplo de las predicciones. Aunque esto es perfectamente válido, en la práctica nos obliga a mantener cierto control sobre las claves ya utilizadas para poder asegurar que no utilizamos claves duplicadas o incorrectas en los nuevos elementos que añadamos a la lista. También nos obliga a implementar la lógica necesaria para generar dichas claves personalizadas.
Firebase nos ofrece un método alternativo que nos facilitará las cosas cuando no necesitemos personalizar a tal nivel las claves de los elementos de nuestras listas. Para ello nos ofrece el método push() que generará por nosotros una clave única y nos devolverá una referencia a ella para que podamos establecer su valor. Estas claves automáticas se basan en el timestamp actual (fecha-hora) por lo que nos asegurará que los elementos de la lista quedarán ordenados por clave de forma cronológica. Veamos un ejemplo para ver que aspecto tienen las claves generadas, vamos a insertar nuevas predicciones a nuestra lista utilizando el método push().
Si observamos los nuevos datos insertados desde la consola de Firebase veremos lo siguiente:
escribir-push
Como podéis comprobar, las claves generadas no nos dicen nada a simple vista, pero nos aseguran que si ordenamos la lista por clave (consulta los métodos de ordenación de Firebase) los elementos quedarán ordenados en orden cronológico de forma ascendente.
En ocasiones puede ser útil conocer la clave que ha asignado Firebase al nuevo elemento insertado, por ejemplo para insertar referencias a dicho elemento en otros lugares de la base de datos. Para ello podemos aprovechar que el método push() devuelve una referencia al nuevo elemento creado, por lo que podemos utilizar su método getKey() para conocer la clave que le ha asignado. Por ejemplo:
Y nos queda un último tema pendiente en relación a la actualización de información sobre la base de datos, la eliminación de contenido. Para eliminar un nodo de nuestra base de datos tenemos dos posibilidades:
  • Utilizar los métodos setValue() o updateChildren() para asignarle el valor null.
  • Utilizar el método removeValue() sobre la referencia que queremos eliminar.
De esta forma, eliminar por ejemplo uno de nuestros días de la semana sería tan sencillo como:

No hay comentarios:

Publicar un comentario