Otto Bus – Android

Un mundo lleno de eventos

El trabajo en una empresa requiere la movilización de gran cantidad de personal para el desarrollo de una sola aplicación: el briefing con el cliente, el análisis de requisitos, la elaboración de la base de datos, los servicios web, picar código, picar código, picar código. Una de las tareas más duraderas del proceso de creación del hardware es la de escribir el código. Es por esto que cualquier cosa que pueda evitarnos picar más código y además, facilitar la labor es bienvenida.

¿Por qué usar eventos? La modularidad


Muchas de las aplicaciones que desarrolla una empresa tienen las mismas funcionalidades, por lo que se puede aprovechar el código de unas a otras. Si ya está hecho, ¿para qué vamos a invertir tiempo en hacerlo otra vez? Y en estrecha relación con este objetivo se encuentra la idea de la modularidad. La modularidad consiste en codificar de manera que el sistema esté compuesto por módulos independientes que, aunque interactúen entre sí, dependan lo mínimo posible los unos de los otros. De esta manera, unos módulos se pueden aprovechar más adelante haciendo cambios mínimos.

Una biblioteca que contribuye a esta modularidad —así como a mantener una comunicación más sencilla entre nuestras clases— es Otto con su clase Bus. Gracias a esta biblioteca, podemos mandar eventos entre clases sin que tengamos que forzar que solo una clase pueda recibir el evento.

Imagina que tenemos la clase CocinaActivity. Para cocinar, CocinaActivity necesita que la clase HornilloFragment llame a sus métodos abrirGas(), producirChispa() y prenderLlama(). Una forma de hacer esto sería darle a CocinaActivity un objeto de la clase HornilloFragment y que esta primera los llamara. Ahora, en el futuro realizamos una aplicación donde podemos reutilizar CocinaActivity, pero por desgracia hay que cambiar todas las referencias a HornilloFragment porque en nuestro proyecto usamos la clase VitroceramicaFragment, y para cocinar, CocinaActivity solo tiene que llamar al método encender() de esta.

Si, en lugar de haberle dado un objeto HornilloFragment, hubiéramos emitido un evento para que lo recogiera la clase correspondiente, ahora solo tendríamos que decirle a VitroceramicaFragment que estuviese pendiente de ese evento para encenderse en lugar de tener que cambiar todas las referencias a HornilloFragment que hay en la clase CocinaActivity.

CocinaActivity diría simplemente “necesito cocinar” y VitroceramicaFragment u HornilloFragment o cualquier otra clase futura, estaría pendiente de cuándo CocinaActivity dice eso para ejecutar los métodos que pertinentes. Incluso podría haber otras veinte clases más esperando a que CocinaActivity quiera cocinar para ejecutar código, por lo que podríamos hacer que esta única orden llegara a todo el que le interesa.

Pues básicamente, con Otto podemos publicar este mensaje emitido por CocinaActivity y recogerlo en cualquier otra clase, igual que un BroadcastReceiver pero sin IntentFilter.

Paso a paso


En este tutorial vamos a aprender a publicar eventos y a suscribirse a ellos, igual que se publica y te suscribes a un boletín de noticias. Así pues, estos son los puntos que cubriremos en esta explicación:

  1. Añadir la biblioteca de Otto a nuestras dependencias y crear una clase singleton
  2. Registrar una actividad para usar un bus
  3. Publicar y subscribirse a eventos

Después de estos tres apartados, veremos un proyecto donde se usa Bus que os podréis descargar y otros puntos interesantes sobre el uso de Otto.

  1. Proyecto
  2. Uso de un evento más realista
  3. ¿Por qué hay que desregistrarlas clases?
  4. Subscribirse a eventos en una clase padre
  5. Producir un evento con @Provide

 

1. Añadir la biblioteca a las dependencias y crear una clase singleton


Para usar Otto debemos compilar la siguiente biblioteca en las dependencias:

La clase Bus de Otto es una de esas clases de las que se debe crear una instancia única utilizando el patrón de desarrollo singleton, de forma que siempre queramos un objeto, no se cree una nueva instancia, sino que siempre usemos el mismo. Para ello crearemos una clase llamada BusProvider siguiendo este patrón. (No obstante, el patrón de desarrollo singleton entorpece bastante la creación de pruebas en la fase de testeo, lo recomendable es utilizar un inyector de dependencias).

El patrón de desarrollo singleton consiste en crear una clase que tenga como atributo estático un objeto de la clase que quereos usar y un método estático que lo inicializa, solo la primera vez que se llama, y lo devuelve, de manera que cada vez que necesitemos una instancia de la clase, llamaremos a este método y siempre obtendremos la misma.

BusProvider.java

Esta clase tiene un único constructor privado sin parámetros para evitar que se creen instancias de la misma, y un atributo de la clase Bus. El método getBus() comprueba si el atributo bus tiene valor. Si no tiene, lo inicializa y por último lo devuelve, así que, como podéis ver, cada vez que llamemos a este método, estaremos obteniendo el mismo objeto.

La instancia de la clase Bus que hemos creado permitirá que se envíen eventos tanto desde hilo principal (UIThread) como desde cualquier otro hilo que creemos. Si queremos obligarlo a que solo se ejecuten eventos en el hilo principal, Bus permite que en el constructor le pasemos el parámetro ThreadEnforcer(MAIN), pero esta solución fallará al intentar publicar un evento desde un hilo secundario que modifique las vistas, por lo que a continuación os dejo otra clase BusProvider que solo ejecuta eventos en el UIThread (user interface thread):

Podréis comprobar que la diferencia está en que en lugar de devolver un objeto de la clase Bus, lo que devolvemos es un objeto que hereda de Bus del cual sobrescribimos su método post() haciendo que todo lo que se publique desde un hilo secundario se envíe al Looper del UIThread.

Si todo esto de los Threads te suena a chino, no te preocupes. Simplemente copia la clase y no te hagas más preguntas. Tu salud mental te lo agradecerá.

La tarea de crear un singleton se ve facilitada por la recién publicada versión 1.3.1 de Android Studio, que incorpora la opción de crear un singleton al pulsar el botón secundario sobre un directorio para crear una clase.

Clic secundario sobre una carpeta, escoger "Nuevo" y luego "Singleton"

Android Studio .1.3.1 tiene la opción de crear una clase “singleton”

 

2. Publicar un evento


Un evento es cualquier objeto que queramos mandar de una clase a otra, ya sea un String, Integer o una clase que nosotros hayamos creado. Para publicar un evento solo necesitamos disponer de una instancia de la clase Bus. En las Activities obtenemos un objeto Bus en el onCreate(), en los Fragments lo hacemos en el onCreateView() y en cualquier otra clase podemos hacerlo en el constructor. Y luego es tan sencillo como llamar al método bus.post() pasándole el objeto que queremos publicar:

Así, por ejemplo, en la clase CocinaActivity publicamos un String:

CocinaActivity.java

Cuando llamemos al método cocinar(), se publicará un evento (en este caso un objeto String).

 

3. Subscribirse a un evento


Para recibir eventos es necesario registrar la clase en el Bus. Registrar es una forma de decirle al bus, “Hola, quiero enterarme de lo que dicen las otras clases”, igual que registras en una página de noticias para recibir sus noticias. Si no queremos recibir eventos en esa clase, no registramos la clase, ya que esto es memoria desperdiciada.

En el caso de una Activity o de un Fragment, registramos la clase en el onResume() y la desregistramos en el onPause(). Más adelante se da una explicación sobre por qué hay que desregistrar la clase.

Esta parte no tiene mayor complicación. Entonces, como ahora podemos subscribirnos a eventos, vamos a suscribirnos al que ha publicado la clase CocinaActivity. En la clase VitroceramicaFragment creamos un método con la anotación @Subscribe que reciba el mismo tipo de objeto publicado, es decir, un String:

La clase CocinaActivity ha publicado un objeto String. Entonces, cualquier clase que contenga un método con la anotación @Subscribe y cuyo único parámetro formal sea del tipo String, recibirá dicha objeto cuando sea publicado. En este insulso ejemplo, VitroceramicaFragment muestra con un Toast el objeto String que le ha mandado CocinaActivity. Si hubiera cuarenta clases con un método que se suscribe a recibir un String, las 40 lo recibirán. El nombre del método con la anotación @Subscribe es indiferente. Lo único importante es que el parámetro formal sea del mismo tipo que el publicado.

 

4. Código



activity_main.xml


vitroceramica_fragment_layout.xml


MainActivity.java


VitroceramicaFragment.java

Podéis descargarlo de aquí

En este proyecto simple, CocinaActivity lanza un evento al pulsar el botón “Cocinar”. VitroceramicaFragment lo recibe y se enciende, y a su vez publica un evento anunciando que ya está encendido. Este es recibido por CocinaActivity, quien cambia el texto del botón a “No Cocinar”. Para ello ha sido necesario también registrar CocinaActivity y deregistrarla como se ha explicado antes. Al pulsar de nuevo el botón, mandará un evento para que se apague y los mensajes volverán a cambiar.

Imagen del proyecto PruebaOtto. Vitrocerámica apagada.

Estado inicial

Imagen del proyecto PruebaOtto. Vitrocerámica encendida.

Al pulsar el botón, se reciben los eventos y cambian los textos.

Es importante que no nos confundamos al importar la clase Subscribe con la clase Subscribe de Guava en caso de que también tengamos esa biblioteca compilada.

5. Uso de un evento más realista


Naturalmente, nunca vamos a publicar un String o un Integer. De forma profesional, cuando queremos lanzar un evento, se crea una clase concreta con el sufijo “Event”, que contenga los objetos que necesitamos pasar y se publica siempre una nueva instancia.

Ejemplo:

En la clase que fuera, al cambiar la información del usuario, mandaríamos este evento así:

Y en la clase receptora lo recogeríamos así:

Es una buena práctica comprobar que lo que recibimos no es nulo cuando usamos eventos que traen datos desde un servicio web.

 

6. ¿Por qué hay que desregistrar las clases?


No es obligatorio, pero sí que es importante desregistrar las clases porque, si no, se estaría produciendo una pequeña fuga de memoria. El Bus tiene una lista de clases que se registran. Si registramos una Activity al iniciarse y no la desregistramos al finalizar, cuando vuelva a iniciarse, se volverá a registrar y la lista será cada vez mayor.

 

7. Subscribirse a eventos en una clase padre


El propio Jake Wharton, autor de esta biblioteca, explica en una publicación de GitHub (https://github.com/square/otto/issues/26) que los método con anotaciones @Subscribe en una clase padre no instanciada directamente no funcionarán, pero que esto se debe a las propias limitaciones de Java. Por ejemplo, tenemos una clase MyActivity que hereda de MyBaseActivity y en esta última hacemos un método con una anotación @Subscribe. Si instanciamos MyActivity, ese método que hemos puesto en la clase padre no funcionará aunque registremos MyActivity en el Bus. SI por el contrario instanciamos MyBaseActivity, sí. Aunque es preferible no hacerlo así. si queremos sortear este problema y hacer que funcionen los métodos @Subscribe en una clase padre no instanciada, la solución consiste en declara el método como un atributo de la clase de tipo Object, registrarlo en el bus en el onResume() y desregistrarlo en el onPause() de la siguiente manera:

 

8. La anotación @Produce


Otra forma adicional de publicar eventos es utilizando la anotación @Produce al principio de un método que devuelva algo. Este método será llamado inmediatamente al ser creada la Activity y se publicará el objeto que devuelva dicho método. Ejemplo:

Esto publicará un objeto String tan pronto como la Activity quede cargada y será recogido por los métodos que estén subscritos a String. Dentro del método podemos poner cualquier cosa que queramos ejecutar para devolver un objeto u otro. De nuevo, el nombre del evento no importa. Lo importante es que donde nos queramos subscribir recibamos un objeto String como parámetro formal del método.

0 Comentarios

Contesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

*

©2017 Codictados Comunidad libre para el aprendizaje de codigo Online

o

Inicia Sesión con tu Usuario y Contraseña

o    

¿Olvidó sus datos?