martes, 12 de mayo de 2015

Notificaciones en Android

     Las notificaciones de la barra de estado

La barra de estado de Android se encuentra situada en la parte superior de la pantalla. La parte izquierda de esta barra está reservada para visualizar notificaciones. Cuando se crea una nueva notificación, aparece un texto desplazándose en la barra, y a continuación, un pequeño icono permanecerá en la barra para recordar al usuario la notificación.

El usuario puede arrastrar la barra de notificaciones hacia abajo, para mostrar el listado de las notificaciones por leer. Un posible ejemplo se muestra a continuación:

Una notificación puede ser creada por un servicio o por una actividad. Aunque dado que la actividad dispone de su propio interfaz de usuario, parece que las notificaciones son el mecanismo de interacción más interesante del que disponen los servicios. Las notificaciones pueden crearse desde un segundo plano, sin interferir con la actividad que en ese momento esté utilizando el usuario.

Ejercicio paso a paso: Las notificaciones de la barra de estado
1. Abre el proyecto ServicioMusica.
2. Declara las siguientes variables al comienzo de la clase:
private NotificationManager nm;  
private static final int ID_NOTIFICACION_CREAR = 1;
3. Vamos a obtener una referencia al NotificationManager del sistema.  Para ello, declara las siguientes variables y añade al método onCreate() la siguiente línea:
  nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);       
4. Vamos a crear una nueva notificación. Añade en el método onStartCommand() la siguiente línea:
     
Notification notificacion = new Notification(
                        R.drawable.icon,
                        "Creando Servicio de Música",
                        System.currentTimeMillis() );
Como puedes ver, en el constructor de una notificación hay que indicar 3 parámetros: el icono a visualizar (en el ejemplo usamos el mismo que el de la aplicación. NOTA: En MOTODEV se llamaic_laucher), el texto a mostrar y la fecha que se visualizará asociada a la notificación (en el ejemplo indicamos que ahora mismo).
5. Define información adicional que será utilizada en la ventana de notificaciones. Esta información incluye el mensaje expandido y la actividad a ejecutar cuando se pulse sobre la notificación:
PendingIntent intencionPendiente = PendingIntent.getActivity(
          this, 0, new Intent(this, ActividadPrincipal.class), 0);
notificacion.setLatestEventInfo(this"Reproduciendo música",
       "información adicional", intencionPendiente);
Cuando el usuario abra la ventana de notificaciones, podrá ver información adicional formada por un título y un texto explicativo. Además se podrá asociar una actividad para que se ejecute cuando el usuario pulse sobre la notificación. En el ejemplo se crea un PendingIntent asociado a la actividad ActividadPrincipal.Por supuesto, también puedes crear una nueva actividad para usarla exclusivamente con este fin. En un ejemplo más complejo, puedes pasar los parámetros adecuados a través del Intent, para que la actividad conozca los detalles específicos que provocaron la notificación (por ejemplo, el número de teléfono que provocó la llamada perdida). 
Una notificación puede tener otros parámetros, por ejemplo, puede reproducir un sonido, puede hacer vibrar el teléfono o puede hacer parpadear un LED del teléfono. Puedes consultar el siguiente punto si estás interesado en alguno de estos aspectos.
6. Vamos a pasar la notificación creada al NotificationManager. Para ello añade:
       nm.notify(ID_NOTIFICACION_CREAR, notificacion);      
       ... // resto de código de onStartCommand()
7. Queremos que si el servicio deja de estar activo, eliminamos la notificación. Para ello añade enonDestroy():
nm.cancel(ID_NOTIFICACION_CREAR);
Este paso es opcional. Muchas notificaciones han de permanecer visibles aunque el servicio que las creo sea destruido. En nuestro caso, dado que estamos anunciando que un servicio de reproducción de música está activado, la notificación deja de tener sentido al desaparecer el servicio.
Práctica: Uso del servicio de música en Asteroides
  1. Copia la clase ServicioMusica realizada en el ejercicio anterior al proyecto Asteroides.
  2. Corrige los errores que hayan aparecido para adaptarla al nuevo proyecto. NOTA: Mantén la versión del SDK de Asteroides en 1.6.
  3. En el ejercicio anterior, cuando se visualiza los detalles de la notificación en el ejercicio anterior se podía lanzar la actividad ActividadPrincipal.  Modifica el código para que se lace la actividad Asteroides.
  4. Si realizas el punto anterior simplemente lanzando la actividad Asteroides, cuando el usuario pulse sobre la notificación el sistema lanzará una nueva tarea, aunque ya exista una previa. Si te interesa que no se lance una nueva tarea cuando ya exista una previa añade la línea en negrita en AndroidManifest.xml
 <activity
            android:name=".Asteroides"
            android:label="@string/app_name"
            android:launchMode="singleTask">
  1. Lanza el servicio en el método onCreate() de la actividad Asteroides. Para el servicio en el método onDestroy().
Fuente: http://www.androidcurso.com/index.php/tutoriales-android-fundamentos/38-unidad-8-servicios-notificaciones-y-receptores-de-anuncios/161-las-notificaciones-de-la-barra-de-estado

Android Ciclo de vida de un servicio.

Ciclo de vida de un servicio.

Ciclo de vida de un servicio

Es importante que recuerdes que un servicio tiene un ciclo de vida diferente a una actividad. A continuación, podemos ver un gráfico que ilustra el ciclo de vida de los servicios:

Figura 6: Ciclo de vida de los servicios.
Como acabamos de explicar existen dos tipos de servicios según como hayan sido creados. Las funciones de estos servicios son diferentes, y por lo tanto, también su ciclo de vida.
Si el servicio es iniciado mediante startService() el sistema comenzará creándolo y llamando a su método onCreate()A continuación llamará a su método onStartCommand(Intent intent, int flags, int startId) [1] con los argumentos proporcionados por el cliente. El servicio continuará en ejecución hasta que sea invocado el método stopService() o stopSelf().
NOTA: Si se producen varias llamadas a startService() no supondrá la creación de varios servicios, aunque sí que se realizarán múltiples llamadas a onStartCommand(). No importa cuántas veces el servicio haya sido creado, parará con la primera invocación de stopService() o stopSelf(). Sin embargo, podemos utilizar el método stopSelf(int startId) para asegurarnos que el servicio no parará hasta que todas las llamadas hayan sido procesadas.
Cuando se inicia un servicio para realizar alguna tarea en segundo plano, el proceso donde se ejecuta podría ser eliminado ante una situación de baja memoria. Podemos configurar la forma en que el sistema reaccionará ante esta circunstancia según el valor que devolvamos en onStartCommand(). Existen dos modos principales: devolveremos START_STICKY si queremos que el sistema trate de crear de nuevo el servicio cuando disponga de memoria suficiente. Devolveremos START_NOT_STICKY si queremos que el servicio sea creado de nuevo solo cuando llegue una nueva solicitud de creación.
Teniendo en cuenta que los servicios pueden estar largo tiempo en ejecución, el ciclo de vida del proceso que contiene nuestro servicio es un asunto de gran importancia. Conviene aclarar que en situaciones donde el sistema necesite memoria conservar un servicio siempre será menos prioritario que la actividad visible en pantalla, aunque más prioritario que otras actividades en segundo plano. Dado que el número de actividades visibles es siempre reducido, un servicio solo será eliminado en situaciones de extrema necesidad de memoria. Por otra parte, si un cliente visible está conectado a un servicio, el servicio también será considerado como visible, siendo tan prioritario como el cliente. En el caso de un proceso que contenga varios componentes, por ejemplo una actividad y un servicio, su prioridad se obtiene como el máximo de sus componentes.
Podemos también utilizar bindService(Intent servicio, ServiceConnection conexion, int flags) para obtener una conexión persistente con un servicio. Si dicho servicio no está en ejecución, será creado (siempre que el flag BIND_AUTO_CREATE esté activo), llamándose al método onCreate(), pero no se llamará a onStartCommand()En su lugar se llamará al método onBind(Intent intencion)  que ha de devolver al cliente un objeto IBinder a través del cual se podrá establecer una comunicación entre cliente y servicio. Esta comunicación se establece por medio de una interfaz escrita en AIDL, que permite el intercambio de objetos entre aplicaciones que corren en procesos separados. El servicio permanecerá en ejecución tanto tiempo como la conexión esté establecida, independientemente de que se mantenga o no la referencia al objeto IBinder.
También es posible diseñar un servicio que pueda ser arrancado de ambas formas (startService() y bindService()). Este servicio permanecerá activo se ha sido creado desde la aplicación que lo contiene o si recibe conexiones desde otras aplicaciones.
Todo servicio terminará llamando al método ancestro() cuando vaya a terminar de forma efectiva.

[1] En versiones del API inferiores a 2.0 el método llamado será onStart(). En versiones recientes se mantiene por razones de compatibilidad.

Jesús Moreno - Ingeniero Ténico Informático - consultor Informático

Hola, soy Jesús Moreno Ingeniero Técnico Informático en sistemas por la US y propietario de éste blog. Mi trabajo en los ultimos años se ...