Tutoriales Android

Introducción

Las aplicaciones que hemos creado hasta el momento estaban formadas por una serie de actividades, cada una de las cuales permitía construir un elemento de interacción con el usuario. Una aplicación en Android va a disponer de otros tipos de componentes que serán estudiados en este capítulo.

Cuando necesites que parte de una aplicación se ejecute en segundo plano, debajo de otras actividades, y además no precise de ningún tipo de interacción con el usuario, la opción más adecuada es crear un servicio. Un servicio puede estar en ejecución indefinidamente, o podemos controlarlo desde una actividad. A lo largo de este capítulo aprenderemos las facilidades proporcionadas por Android para la creación de servicios.

Por otra parte, las notificaciones de la barra de estado constituyen un mecanismo de comunicación vital en Android. Permite a las aplicaciones que corren en un segundo plano adviertan al usuario sobre alertas, avisos o cualquier tipo de información. Las notificaciones se representan como pequeños iconos en la barra superior de la pantalla y se utilizan habitualmente para indicar la llegada de un mensaje, una cita de calendario, una llamada perdida o cualquier otra incidencia de interés al usuario. Se trata de una comunicación que no requiere una interacción inmediata del usuario; este puede estar utilizando otra aplicación sin ser interrumpido o puede no estar utilizando el teléfono en ese momento. Este hecho hace de las notificaciones un mecanismo de comunicación ideal para un servicio o receptores de anuncios. Por lo tanto, este capítulo parece el sitio ideal para describir cómo podemos crear nuestras propias notificaciones y utilizarlas desde nuestras aplicaciones.

Terminaremos el capítulo estudiando otro componente de una aplicación Android, los receptores de anuncios. Un receptor de anuncios (BroadcastReceiver en inglés) permite realizar acciones cuando se producen anuncios globales de tipo broadcastExisten muchos anuncios originados por el sistema;como por ejemplo Batería baja, llamada entrante,... Aunque, las aplicaciones también puede lanzar unanuncio broadcast o incluso crear nuevos tipos. Los receptores de anuncios te permitirán crear aplicaciones mucho más integradas en el entorno donde se ejecutan.

Objetivos:

  • Describir el uso de servicios en Android.
  • Enumerar los pasos a seguir cuando queramos crear un servicio para que una tarea se ejecute en segundo plano.
  • Mostrar cómo se las notificaciones de la barra de estado pueden ser utilizadas como mecanismo de comunicación eficaz con el usuario.
  • Repasar los tipos de avisos que pueden utilizar las notificaciones.
  • Describir el uso un servicio como mecanismo de comunicación entre aplicaciones.
  • Enumerar los pasos a seguir para crear un receptor de anuncios.
  • Enumerar los receptores de anuncios más importantes disponibles en Android

{jcomments on}

     Introducción a los servicios en Android

Puedes ver una explicaión de esta sección en formato poli[Media]

Los servicios en Android

 

Puedes ver una explicaión de esta sección en formato poli[Media]

Un servicio para ejecución en segundo plano

 

En muchos casos, será necesario añadir un nuevo componente a tu aplicación para ejecutar algún tipo de acción que se ejecute en segundo plano, es decir, que no requiera una interacción directa con el usuario; pero que queramos que permanezca activo aunque el usuario cambie de actividad. Este es el momento de crear un servicio.

En Android los servicios tienen una doble función:

La primera función permite indicar al sistema que el elemento que estamos creando ha de ejecutarse en segundo plano, normalmente durante un largo período de tiempo. Este tipo de servicios son iniciados mediante el método startService(), que indica al sistema que lo ejecute de forma indefinida hasta que alguien le indique lo contrario.

La segunda función permite que nuestra aplicación se comunique con otras aplicaciones, para lo cual ofreceremos ciertas funciones que podrán ser llamada desde otras aplicaciones. Este tipo de servicios son iniciados mediante el método bindService(), que permite establecer una conexión con el servicio e invocar alguno de los métodos que son ofrecidos.

 

Cada vez que un servicio es creado por alguna de las razones anteriores, el sistema instancia el servicio y llama al método onCreate(). Corresponde al servicio implementar el comportamiento adecuado, habitualmente creará un hilo de ejecución (thread) secundario donde se realizará el trabajo.

Un servicio en sí es algo muy simple, en este capítulo se verán ejemplos de servicios locales escritos en muy pocas líneas. No obstante, también pueden complicarse, como veremos al final del capítulo cuando tratemos de invocar servicios remotos por medio de una interfaz AIDL.

Un servicio, como el resto de componentes de una aplicación, se ejecuta en el hilo principal del proceso de la aplicación. Por lo tanto, si el servicio necesita un uso intensivo de CPU o puede quedar bloqueado en ciertas operaciones, como uso de redes, debes crear un hilo diferente para ejecutar estas acciones. También puedes utilizar la clase IntentService para lanzar un servicio en su propio hilo. 

 


{jcomments on}

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.

 

 

Permisos

Permisos

Podemos conseguir que el acceso global a un servicio declararlo en la etiqueta <service> deAndroidManifest.xml. También podemos definir un permiso para restringir su acceso. En este caso, las aplicaciones han de declarar este permiso, con el correspondiente <uses-permission> en su propio manifiesto.

Podemos definir un permiso para arrancar, parrar o conectarse a un servicio. De forma adicional, podemos restringir el acceso a funciones específicas de las ofertadas por un servicio. Para este propósito, podemos llamar al principio de nuestra función a checkCallingPermission(String) para verificar si el cliente dispone de un permiso en concreto. Para más información sobre permisos se recomienda la lectura del CAPÍTULO 7.

      Un servicio para ejecución en segundo plano.

Ejercicio paso a paso: Un servicio para ejecución en segundo plano de reproducción de música.

Veamos un ejemplo de servicio que corre en el mismo proceso de la aplicación que lo utiliza. El servicio será creado con la finalidad de reproducir una música de fondo y podrá ser arrancado y detenido desde la actividad principal.

1. Crea un nuevo proyecto con los siguientes datos:

Project Name: ServicioMusica

Package Name: org.example.serviciomusica

Minimun Requiered SDK: API 7: Android 2.1 (Eclair)

Compile With: API 17: Android 4.2

Activity Name: MainActivity  (valor por defecto)

Layout Name: activity_main  (valor por defecto)

2. Reemplaza el código del layout activity_main.xml por:

<LinearLayout

   xmlns:android="http://schemas.android.com/apk/res/android"

   android:orientation="vertical"

   android:layout_width="fill_parent"

   android:layout_height="fill_parent">

   <TextView android:layout_width="fill_parent"

                android:layout_height="wrap_content"

                android:text="Servicio de reproducción de música"/>

   <Button android:id="@+id/boton_arrancar"

              android:layout_width="wrap_content"

               android:layout_height="wrap_content"

              android:text="Arrancar servicio"/>

   <Button android:id="@+id/boton_detener"

              android:layout_width="wrap_content"

              android:layout_height="wrap_content"

              android:text="Detener servicio"/>

</LinearLayout>

Se trata de un layout muy sencillo, con un texto y dos botones:

3. Reemplaza el código de la actividad por:

public class MainActivity extends Activity {

       @Override

       public void onCreate(Bundle savedInstanceState) {

             super.onCreate(savedInstanceState);

             setContentView(R.layout.activity_main);

            

             Button arrancar = (Button) findViewById(R.id.boton_arrancar);

             arrancar.setOnClickListener(new OnClickListener() {

                    public void onClick(View view) {

                           startService(new Intent(MainActivity.this,

                                        ServicioMusica.class));

                    }

             });

             Button detener = (Button) findViewById(R.id.boton_detener);

             detener.setOnClickListener(new OnClickListener() {

                    public void onClick(View view) {

                           stopService(new Intent(MainActivity.this,

                                        ServicioMusica.class));

                    }

             });

       }

}

4. Crea la nueva clase, ServicioMusica, con el siguiente código:

public class ServicioMusica extends Service {

       MediaPlayer reproductor;

 

       @Override

       public void onCreate() {

             Toast.makeText(this,"Servicio creado", 
                                                                                Toast.
LENGTH_SHORT).show();

             reproductor = MediaPlayer.create(this, R.raw.audio);

       }

 

       @Override

       public int onStartCommand(Intent intenc, int flags, int idArranque) {

             Toast.makeText(this,"Servicio arrancado "+ idArranque,
                                                                                Toast.
LENGTH_SHORT).show();

             reproductor.start();

             return START_STICKY;

       }

 

       @Override

       public void onDestroy() {

             Toast.makeText(this,"Servicio detenido", 
                                                                                Toast.
LENGTH_SHORT).show();

             reproductor.stop();

       }

 

       @Override

       public IBinder onBind(Intent intencion) {

             return null;

       }

}

5. Edita el fichero AndroidManifest.xml y añade la siguiente línea dentro de la etiqueta <application>.

<service android:name=".ServicioMusica" />

6. Crea una nueva carpeta con nombre raw dentro de la carpeta res. Arrastra a su interior el fichero audio.mp3. NOTA: puedes utilizar cualquier fichero de música compatible con Android siempre que el nombre de fichero sea audio.

7.  Ejecuta la aplicación y comprueba su funcionamiento. Puedes terminar la actividad pulsando el botón de retroceder y verificar que el servicio continua en marcha.  

8. Verifica que aunque pulses varias veces el botón “Arrancar servicio”, este no vuelve a crearse, pero sí que vuelve a llamarse al método onStartCommand(). Además, con solo una vez que pulses en “Detener servicio” este parará.

9. Arranca la aplicación Ajustes / Aplicaciones / Servicios en ejecución / Servicio de Música. Desde aquí puedes obtener información y detener el servicio.NOTA: Esta aplicación no está disponible en todas las versiones.

Los métodos onStartCommand() y onStart() 

Cuando se crea un servicio hay que tener en cuenta una cuestión de compatibilidad. El método onStartCommand() aparece a partir del nivel de API 5, en sustitución de onStart(). Si trabajas con una versión inferior a la 2.0, reemplaza este método por el código siguiente:

Ejercicio paso a paso: Los métodos onStartCommand() y onStart().

1. Comenta el en ejercicio anterior el método onStartCommand()

2. Añade el siguiente método:

@Override

   public void onStart(Intent intent, int startId) {

          Toast.makeText(this, "Servicio arrancado " + startId,

                                                                         Toast.LENGTH_SHORT).show();

          reproductor.start();

   }

3. Verifica que el programa funciona exactamente igual.

4. Comenta el método onStart () y quita el comentario al método onStartCommand().

Si lo comparas con onStartCommand(), este último tiene un parámetro más y permite devolver un resultado. Veámoslos con más detalle, dado que sus parámetros pueden ser utilizados para obtener información valiosa:

public int onStartCommand (Intent intencion, int flags, int idArranque)

Llamado cada vez que un cliente inicializa un servicio mediante el método startService(). Los parámetros se detallan a continuación:

intencion  Un objeto Intent que se indicó en la llamada startService(Intent).

flags  Información sobre como comienza la solicitud. Puede ser 0, START_FLAG_REDELIVERYo START_FLAG_RETRY. Un valor distinto de 0 se utiliza para reiniciar un servicio tras detectar algún problema.

idArranque Un entero único representando la solicitud de arranque específica. Usar este mismo estero en el método stopSelfResult(int idArranque).

 Retorna describe cómo ha de comportarse el sistema cuando el proceso del servicio sea matado una vez que el servicio ya se ha inicializado. Esto puede ocurrir en situaciones de baja memoria. Los siguientes valores están permitidos:

START_STICKY: Cuando sea posible el sistema tratará de recrear el servicio, se realizará una llamada a onStartCommand() pero con el parámetro intencion igual a null. Esto tiene sentido cuando el servicio puede arrancar sin información adicional, como por ejemplo, el servicio mostrado para la reproducción de música de fondo.

START_NOT_STICKY: El sistema no tratará de volver a crear el servicio, por lo tanto el parámetro intencion nunca podrá ser igual a null. Esto tiene sentido cuando el servicio no puede reanudarse una vez interrumpido.

START_REDELIVER_INTENT: El sistema tratará de volver a crear el servicio. El parámetro intencion será el que se utilizó en la última llamada startService(Intent).   


START_STICKY_COMPATIBILITY: Versión compatible de START_STICKY, que no garantiza que onStartCommand() sea llamado después de que el proceso sea matado.

     Las notificaciones de la barra de estado

Puedes ver una explicaión de esta sección en formato poli[Media]

Notificaciones en Android

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 en onDestroy():

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().

 

Configurando tipos de avisos en las notificaciones

Configurando tipos de avisos en las notificaciones

Como hemos comentado una notificación puede utilizar diferentes métodos para alertar al usuario de que se ha producido. Veamos algunas opciones.

Asociar un sonido

Si consideras que una notificación es muy urgente y deseas que el usuario pueda conocerla de forma inmediata, puedes asociarle un sonido que será reproducido cuando se produzca.

El usuario puede definir un sonido por defecto para las notificaciones. Si quieres asociar el sonido de notificaciones por defecto, utiliza la siguiente sentencia:

 notificacion.defaults |= Notification.DEFAULT_SOUND;

Si prefieres reproducir un sonido personalizado para la notificación, puedes almacenarlo en una carpeta y usar:

notificacion.sound = Uri.parse("file:///sdcard/carpeta/tono.mp3");

Si el fichero de audio se encuentra almacenado en el ContentProvider MediaStore, puedes utilizar la siguiente sentencia:

notificacion.sound =
             
 Uri.withAppendedPath(Audio.Media.INTERNAL_CONTENT_URI, "6");

Tendrás que sustituir el parámetro "6" por el ID del elemento que quieras reproducir. Si desconoces este ID, puedes realizar una consulta al ContentProvider. Para más información consulta el apartado 9.7.

Añadiendo vibración

 

También es posible alertar al usuario haciendo vibrar el teléfono.

Puedes utilizar la vibración por defecto:

notificacion.defaults |= Notification.DEFAULT_VIBRATE;

O por el contrario tu propio patrón de de vibración:

long[] vibrate = {0,100,200,300};
notification.vibrate = vibrate;

El array define un patrón de longitudes expresadas en milisegundos; donde el primer valor es el tiempo sin vibrar, el segundo es el tiempo vibrado, el tercero sin vibrar y así sucesivamente. Este arraypuede ser tan largo como queramos, pero solo será activado una vez, no se repetirá de forma cíclica

 

Añadiendo parpadeo de LED

Algunos móviles disponen de diodos LED que pueden ser utilizados para avisar al usuario que se ha producido una notificación. Este método es muy interesante si el grado de urgencia del aviso no es lo suficientemente alto para usar uno de los métodos anteriores.

Podemos utilizar el aviso de LED configurado por defecto:

notificacion.defaults |= Notification.DEFAULT_LIGHTS;

O podemos definir una cadencia de tiempo y color específica para nuestra notificación:

       notificacion.ledARGB = 0xff00ff00;
       notificacion.ledOnMS = 300;
       notificacion.ledOffMS = 1000;
       notificacion.flags |= Notification.FLAG_SHOW_LIGHTS;

 

En el ejemplo anterior se empieza indicando que queremos que el LED se ilumine en color verde, durante 300 ms y luego esté apagado durante 1 segundo. Esta secuencia se repetirá de forma cíclica hasta que el usuario atienda la notificación.

Conviene destacar que no todos los móviles disponen de un LED para este propósito. Además, no todos los colores pueden ser utilizados, siendo el color verde el más habitual para indicar una notificación.

Práctica: Una notificación de socorro

1.     En el proyecto anterior, crea un nuevo botón.

2.     Al pulsar este botón se lanzará una nueva notificación que mostrará el texto “¡SOCORRO!”.

3.     El audio de la notificación será una grabación de voz que diga “¡SOCORRO!”.

4.  La notificación hará vibrar el teléfono con el mensaje internacional de socorro S.O.S. codificado en Morse. Para conseguir esto haz vibrar el teléfono con una sucesión de tres pulsos cortos, tres largos y otros tres cortos (. . . - - - . . .).

{jcomments on}

Receptores de anuncios


Un receptor de anuncios (BroadcastReceiver) recibe y reacciona ante anuncios globales de tipo broadcast. Existen muchos originados por el sistema; como por ejemplo Batería baja, llamada entrante,... Se muestra una tabla mas adelante. Aunque, las aplicaciones también puede lanzar unanuncio broadcast. Los receptores de anuncios no tienen interfaz de usuario, aunque pueden iniciar una actividad o creara una notificación para informar al usuario.

El ciclo de vida de un BroadcastReceiver es muy sencillo solo dispone del método onReceive(). De hecho un objeto BroadcastReceiver sólo existe durante la llamada a onReceive(). El sistema crea el BroadcastReceiver llama a este método y cuando termina destruye el objeto. Un detalle importante es que no hace falta tener la aplicación en marcha donde se define el BroadcastReceiver para que este se active.

El método onReceive() es ejecutado por el hilo principal de la aplicación. Por lo tanto no puede bloquear al sistema (Ver ciclo de vida de una actividad). Si tienes que realizar una acción que puede bloquear al sistema tendrás que lanzar un hilo secundario. Si queremos una acción persistente en el tiempo resulta muy frecuente lanzar un servicio. Desde un BroadcastReceiver no puedes mostrar un cuadro de diálogo o unirse a un servicio (bindService()). Para lo primero, en su lugar puedes lanzar una notificación. Para lo segundo, puede utilizar startService() para arrancar un servicio.

Una aplicación puede registrar un receptor de anuncios de dos maneras: En AndroidManifest.xml y en tiempo de ejecución mediante el método registerReceiver().

Receptor de anuncios registrado en AndroidManifest.xml

Registrar un receptor de anuncios desde AndroidManifest.xml es muy sencillo. No tienes más que introducir las siguientes líneas en AndroidManifest.xml dentro de la etiqueta <aplication>:

<receiver android:name=".ReceptorAnuncio" >

   <intent-filter>

      <action android:name="android.intent.BATTERY_LOW"/>

   </intent-filter>

</receiver>

En segundo lugar tienes que crear la clase ReceptorAnuncio. El método onReceive() será llamado cuando el sistema lance el anuncio broadcast BATTERY_LOW, esto ocurrirá cuando detecte un nivel bajo de batería.

public class ReceptorAnuncio extends BroadcastReceiver {

    @Override

    public void onReceive(Context context, Intent intent) {

        //...

    }

}

 

Ejercicio paso a paso: Un receptor de anuncios

Primero has de realizar el ejercicio: “Las notificaciones de la barra de estado”.

1. Crea un nuevo proyecto con los siguientes datos:

Application Name: Llamada Entrante

Package Name: org.example.llamadaentrante

Minimun Requiered SDK: API 8: Android 2.2 (Froyo)

 

2. Edita AndroidManifest.xml  y añade dentro de la etiqueta <aplication>las siguientes líneas:

<receiver android:name="ReceptorLlamadas" >

    <intent-filter>

<action android:name="android.intent.action.PHONE_STATE" />

    </intent-filter>

</receiver>

De esta forma registramos un receptor de anuncios que se activará cuando se produzca una llamada

3. Tenemos que pedir permiso para leer el estado del teléfono. Añade la siguiente línea dentro de <manifest>.

<uses-permission

             android:name="android.permission.READ_PHONE_STATE"/>

4. Crea una nueva clase con nombre ReceptorLlamadas y el siguiente código:

public class ReceptorLlamadas extends BroadcastReceiver {

       @Override

       public void onReceive(Context context, Intent intent) {

             // Sacamos información del intent

             String estado = "", numero = "";

             Bundle extras = intent.getExtras();

             if(extras != null) {

                    estado = extras.getString(TelephonyManager.EXTRA_STATE);

                    if(estado.equals(TelephonyManager.EXTRA_STATE_RINGING)) {

                           numero = extras.getString( TelephonyManager.EXTRA_INCOMING_NUMBER);

                    }

             }

             String info = estado + " "+ numero;

             Log.d("ReceptorAnuncio", info + " intent="+ intent);

             // Creamos Notificación

             NotificationManager nm = (NotificationManager) context.getSystemService( Context.NOTIFICATION_SERVICE);

             Notification notificacion = new Notification( R.drawable.ic_launcher,"Llamada entrante", System.currentTimeMillis());

             PendingIntent intencionPendiente = PendingIntent.getActivity( context, 0, new Intent(context, LlamadaEntranteActivity.class), 0);

             notificacion.setLatestEventInfo(context, "Llamada entrante", info, intencionPendiente);

             nm.notify(1, notificacion);

       }

}

5. Ejecuta la aplicación e introduce una llamada. Si utilizas el emulador puedes utilizar la vista EmulatorControl.

6. Verifica que se crea la notificación.

7. Abre la notificación y verifica la información mostrada:

8. Con el administrador de tareas detén la aplicación. Verifica que el receptor de anuncios funciona igual aunque la aplicación no esté en marcha.


Recursos adicionales: Algunos anuncios broadcast

Lista de los anuncios broadcast más importantes organizados por temas. (No Manifest): No se puede declarar el receptor de anuncios en AndroidManifest.xml. Solo se puede utilizar registerReceiver(). (Solo sistema): Intención protegida que solo puede ser lanzada por el sistema.

                

Nombre de la acción /(CONSTANTE)

Descripción / Permiso (INFORMACIÓN EXTRA EN INTENT)

Batería

 

android.intent.action.BATTERY_LOW

(ACTION_BATTERY_LOW)

Batería baja

(Solo sistema)

android.intent.action.BATTERY_OKAY

(ACTION_BATTERY_OKAY)

Batería correcta después de haber estado baja

(Solo sistema)

android.intent.action.ACTION_POWER_CONNECTED

(ACTION_POWER_CONNECTED)

La alimentación se ha conectado

(Solo sistema)

android.intent.action.ACTION_POWER_DISCONNECTED (ACTION_POWER_DISCONNECTED)

La alimentación se ha desconectado

(Solo sistema)

android.intent.action.BATTERY_CHANGED

(ACTION_BATTERY_CHANGED)

Cambia el estado de la batería

(No Manifest) (Solo sistema)

Sistema

 

android.intent.action.BOOT_COMPLETED

(ACTION_BOOT_COMPLETED)

sistema operativo cargado.

Permiso RECEIVE_BOOT_COMPLETED

(Solo sistema)

android.intent.action.ACTION_SHUTDOWN

(ACTION_SHUTDOWN)

El dispositivo va a ser desconectado

(Solo sistema)

android.intent.action.AIRPLANE_MODE

(ACTION_AIRPLANE_MODE_CHANGED)

modo vuelo activo

(Solo sistema)

android.intent.action.TIME_TICK (ACTION_TIME_TICK)

Se envía cada minuto.

(No Manifest) (Solo sistema)

android.intent.action.TIME_SET (ACTION_TIME_CHANGED)

La fecha/hora es modificada

(Solo sistema)

android.intent.action.CONFIGURATION_CHANGED

(ACTION_CONFIGURATION_CHANGED)

Cambia la configuración del dispositivo

(orientación, idioma,..)

(No Manifest) (Solo sistema)

Entradas y pantalla

 

android.intent.action.SCREEN_OFF (ACTION_SCREEN_OFF

La pantalla se apaga (Solo sistema)

android.intent.action.SCREEN_ON

(ACTION_SCREEN_ON)

La pantalla se enciende (Solo sistema)

android.intent.action.CAMERA_BUTTON (ACTION_CAMERA_BUTTON)

Se pulsa el botón de la cámara.

(EXTRA_KEY_EVENT)

android.intent.action.HEADSET_PLUG (ACTION_HEADSET_PLUG)

Se conectan los auriculares

(extras: state, name, microphone)

android.intent.action.INPUT_METHOD_CHANGED (ACTION_INPUT_METHOD_CHANGED)

Cambia método de entrada

android.intent.action.USER_PRESENT (ACTION_USER_PRESENT)

El usuario está presente después de que se active el dispositivo (Solo sistema)

Memoria y Escaner Multimedia

 

android.intent.action.DEVICE_STORAGE_LOW (ACTION_DEVICE_STORAGE_LOW)

Queda poca memoria

(Solo Sistema) (Solo sistema)

android.intent.action.DEVICE_STORAGE_OK (ACTION_DEVICE_STORAGE_OK)

Salimos de la condición de poca memoria

(Solo Sistema) (Solo sistema)

android.intent.action.MEDIA_EJECT (ACTION_MEDIA_EJECT)

El usuario pide extraer almacenamiento exterior

android.intent.action.MEDIA_MOUNTED (ACTION_MEDIA_MOUNTED)

Almacenamiento exterior disponible

android.intent.action.MEDIA_REMOVED (ACTION_MEDIA_REMOVED)

Almacenamiento exterior no disponible

android.intent.action.MEDIA_SCANNER_FINISHED (ACTION_MEDIA_SCANNER_FINISHED)

El escáner de medios termina un directorio (se indica en Intent.mData)

android.intent.action.MEDIA_SCANNER_SCAN_FILE (ACTION_MEDIA_SCANNER_SCAN_FILE)

El escáner de medios encuentra un fichero (se indica en Intent.mData)

android.intent.action.MEDIA_SCANNER_STARTED (ACTION_MEDIA_SCANNER_STARTED)

El escáner de medios comienza un directorio (se indica en Intent.mData)

Aplicaciones

 

android.intent.action.MY_PACKAGE_REPLACED (ACTION_MY_PACKAGE_REPLACED)

Una nueva version de tu aplicación ha sido instalada

(Solo sistema)

android.intent.action.PACKAGE_ADDED (ACTION_PACKAGE_ADDED)

Una nueva aplicación instalado

(EXTRA_UID, EXTRA_REPLACING)

(Solo sistema)

android.intent.action.PACKAGE_FIRST_LAUNCH (ACTION_PACKAGE_FIRST_LAUNCH)

Primera vez que se lanza una aplicación

(Solo sistema)

android.intent.action.PACKAGE_REMOVED (ACTION_PACKAGE_REMOVED)

Se desinstala una aplicación

(Solo sistema)

Comunicaciones y redes

 

android.intent.action.PHONE_STATE

llamada de teléfono. Permiso: READ_PHONE_STATE(EXTRA_STATE,EXTRA_STATE_RINGING)

android.intent.action.NEW_OUTGOING_CALL (ACTION_NEW_OUTGOING_CALL)

Se va ha hacer una llamada. Permiso  PROCESS_OUTGOING_CALLS(EXTRA_PHONE_NUMBER) (Solo sistema)

android.provider.Telephony.SMS_RECEIVED

SMS recibido. Permiso: RECEIVE_SMS

android.bluetooth.intent.action.DISCOVERY_STARTED

comienza escáner Bluetooth

android.bluetooth.intent.action.ENABLED

Bluetooth habilitado

android.net.wifi.NETWORK_IDS_CHANGED (NETWORK_IDS_CHANGED_ACTION)

Cambia la red WiFi

android.net.wifi.STATE_CHANGE (NETWORK_STATE_CHANGED_ACTION)

Cambia la conectividad WiFi (EXTRA_NETWORK_INFO, EXTRA_BSSID, EXTRA_WIFI_INFO)

android.net.wifi.RSSI_CHANGED (RSSI_CHANGED_ACTION)

Cambia el nivel de señal WiFi (EXTRA_NEW_RSSI)


{jcomments on}

Arrancar una actividad en una nueva tarea desde un receptor de anuncio

El concepto de tarea no había sido introducido en el curso. Sin embargo, resulta sencillo y seguro que si eres usuario de Android estás familiarizado con él. La forma más sencilla de entenderlo es que pulses en tu dispositivo móvil el botón de Casa durante un segundo. Se mostrará la lista de tareas que hay actualmente en ejecución o que fueron ejecutadas recientemente. Puedes intercambiar de tarea sin más que pulsar sobre una de las previsualización que aparece en pantalla.

No hay que confuncir el concepto de tarea con el de aplicación. Para iniciar una nueva tarea puedes pulsar al botón de Casa y pulsar sobre uno de los iconos de la pantalla inicial. De esta forma se iniciará la aplicación correspondiente, por ejemplo el lector de correo. Desde esta tarea se pueden arrancar nuevas aplicaciones, por ejemplo, desde un correo podemos acceder a una URL ejecutando el navegador Web. Esta nueva aplicación se ejecutará en la misma tarea.

Otro aspecto a destacar es que cada tarea tiene una pila de actividades independiente. Es decir, si pulsamos en botón de Volver en la tarea descrita pasaremos de nuevo al lector de correo. Pero, si cambiamos de tarea y pulsamos el botón de Volver el resultado será muy diferente.

Ejercicio paso a paso: Arranque de una actividad al llegar un SMS.

 

Vamos a modifica el proyecto Asteroides para que se arranque automáticamente la actividad AcercaDeal llegar un SMS cualquiera.

1.     Abre el proyecto Asteroides.

2.     En AndroidManifest.xml pide el permiso adecuado y registra el receptor de anuncios:       

<uses-permission android:name ="android.permission.RECEIVE_SMS "/>

<aplication>

<receiver android:name="ReceptorSMS" >

   <intent-filter>

      <action android:name="android.provider.Telephony.SMS_RECEIVED"/>

   </intent-filter>

</receiver>

</aplication>

3.     Crea una nueva clase con el siguiente código:


public class ReceptorLlamadas extends BroadcastReceiver {

@Override

public void onReceive(Context context, Intent intent) {

  Intent i = new Intent(context, AcercaDe.class);

             i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

             context.startActivity(i);

       }

}

La forma de arrancar una actividad desde un receptor de anuncios es muy similar  a la que estudiamos en el capítulo 3. La única diferencia es que ahora hemos necesitado añadir un flag a la intención, que indica que la actividad sea creada en una nueva tarea.

4.  Ejecuta la aplicación. Envia un SMS al dispositivo y verifica que se habre la actividad Acerca de …

NOTA: Si utilizas el emulador puedes utilizar la vista EmulatorControl.para mandar simular el envio de un SMS. Si utilizas un dispositivo real o un emulador Genymotion puedes descargarte la siguiente aplicación Sms emulator (https://play.google.com/store/apps/details?id=act.main ).

Cuando lanzamos una nueva actividad, Android nos permite controlar en que tarea y en que posición de la pila se situará. No obstante, se recomienda usar siempre el sitema estándar. Es decir, si lanzamos una nueva actividad desde otra actividad, la nueva actividad se situa en la misma tarea en la cima de la pila de actividades. Cuestión diferente es lanzar la actividad desde un receptor de anuncios, dado que cuando llege el SMS podemos encontrarno en cualquier tarea. En este caso, resulta imprescindible activar el flag FLAG_ACTIVITY_NEW_TASK, así, en caso de no existir la tarea esta podrá ser creada.

Enlaces de interes:

Lanzar las actividades de laa forma estándar suele ser lo más adecuado en la mayoría de los casos. No obstante, si quieres profundzar sobre este tema te recomendamos los siguientes enlaces:

·         Tasks and Back Stack:Documentación oficial de Android:

http://developer.android.com/guide/components/tasks-and-back-stack.htm

·         Manipulating Android tasks and back stack:Presentación didáctica con muchos ejemplos:

http://es.slideshare.net/RanNachmany/manipulating-android-tasks-and-back-stack

 

Lee la siguiente sección del libro "El gran libro de Android",  para saber más:

8.4.3 Arrancar un servicio tras cargar el sistema operativo

{jcomments on}

Un servicio en un nuevo hilo con IntentService

A la hora de diseñar aplicaciones en Android hay que tener muy en cuenta que todos los componentes (actividades, servicios y receptores de anuncios) se van a ejecutar en el hilo principal de la aplicación. Dado que este hilo ha de estar siempre disponible para atender los eventos generados por el usuario, nunca debe ser bloqueado. Es decir cualquier proceso que requiera un tiempo importante no ha de ser ejecutado desde este hilo. En su lugar hay que crear un nuevo hilo para que realice este proceso y así dejar libre al hilo principal para que este pueda seguir procesando nuevos eventos.

Podemos crear un nuevo hilo utilizando la clase estándar de Java Thread, tal y como se explicó en el capítulo 5. Para automatizar este proceso, Android nos proporcina la clase AsyncTask. Tambien nos proporciona la clase IntentService, cuando queramos lanzar un servicio en un nuevo hilo. En este apartado veremos que ocurre cuando un servicio bloquea el hilo principal y como solucionarlo mediante la clase IntentService.

Ejercicio paso a paso: Un servicio que bloquea el hilo principal

Muchos servicios han de realizar costosas operaciones o han de esperar a que concluyan lentas operaciones en la red. En ambos casos hay que tener la precaución de no bloquear el hilo principal. De hacerlo el resultado puede ser catastrófico, como se muestra en este ejercicio. 

1.     Crea un nuevo proyecto con los siguientes datos:

Project name: IntentService

Package name: com.example.intentservice

Create Activity: MainActivity

Min SDK Version: 7

2.     Reemplaza el código del layout principal por:

<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" >

   <LinearLayout

      android:layout_width="match_parent"

      android:layout_height="wrap_content" >

      <EditText

         android:id="@+id/entrada"

         android:layout_width="0dip"

         android:layout_height="wrap_content"

         android:layout_weight="1"

         android:inputType="numberDecimal"

         android:text="2.2" >

         <requestFocus/>

      </EditText>

      <Button

         android:layout_width="wrap_content"

         android:layout_height="wrap_content"

         android:onClick="calcularOperacion"

         android:text="Calcular operación"/>

   </LinearLayout>

   <TextView

      android:id="@+id/salida"

      android:layout_width="match_parent"

      android:layout_height="match_parent"

      android:text=" "

      android:textAppearance="?android:attr/textAppearanceMedium"/>

</LinearLayout>

 

3.     Reemplaza el código de MainActivity por el siguiente:

public clas sMainActivity extends Activity {

       private EditText entrada;

       public staticTextView salida;

 

       @Override

       public void onCreate(Bundle savedInstanceState) {

             super.onCreate(savedInstanceState);

             setContentView(R.layout.activity_main);

             entrada= (EditText) findViewById(R.id.entrada);

             salida= (TextView) findViewById(R.id.salida);

       }

 

       public void calcularOperacion(View view) {

             double n = Double.parseDouble(entrada.getText().toString());

             salida.append(n +"^2 = ");

             Intent i = new Intent(this, ServicioOperacion.class);

              i.putExtra("numero", n);

             startService(i);

       }

}
 

Observa como la variable salida ha sido declarada como public static. Esto nos permitirá acceder a esta variable desde otras clases. El método calcularOperacion() será llamado cuando se pulse el botón. Comienza obteniendo el valor real introducido en entrada. Se muestra la operación a realizar por salida. Luego, se crea una nueva intención con nuestro contexto y la clase que acabamos de crear. A continuación y se le añade un extra con el valor introducido. Finalmente, se arranca el servicio.

4.     Crea la clase ServicioOperacion con el siguiente código:

public class ServicioOperacion extends Service {

       @Override

       public int onStartCommand(Intent i, int flags, int idArranque){

             doublen = i.getExtras().getDouble("numero");

             SystemClock.sleep(5000);

             MainActivity.salida.append(n*n + "\n");

             return START_STICKY;

       }

 

       @Override

       public IBinder onBind(Intent arg0) {

             return null;

Cuando se arranca el servicio se llamará al método onStartCommand. Este comienza obteniendo el valor a calcular a partir de un extra. Luego vamos a simular que se realizan un gran número de operaciones para ello vamos a bloquear el hilo durante 5000 ms (5 segundos) utilizando el método sleep. Una vez terminado el resultado se muestra directamente en elTextView salida. Esta forma de trabajar no resulta muy recomendable. Se ha realizado así, para ilustrar como es posible acceder al sistema gráfico de Android dado que estamos en el hilo principal. Finalmente, devolvemos START_NOT_STICKY para indicar al sistema que si por fuerza mayor ha de destruir el servicio, no hace falta que lo vuelva a crear.

5.     Ejecuta la aplicación. El resultado ha de ser similar al siguiente:

Observa como mientras se realiza la operación el usuario no puede pulsar el botón ni modificar el EditText. El usuario tendrá la sensación de que la aplicación está bloqueada.

6.     Modifica el tiempo de retardo para que este sean 25 seg. (sleep(25000)). Ejecuta de nuevo la aplicación y observa como el sistema nos mostrará en siguiente error:

Para que esto no bloquee el hilo principal podemos utilizar un IntentService. En el siguiente ejercicio mostraremos como realizarlo.

 

La clase intentService

Utilizaremos la clase IntentService en lugar de Service cuando queramos un servicio que se ejecute en su propio hilo. Esta clase tiene un constructor donde hay que indicar el nombre que queremos dar al servicio. Lo habitual va  a ser que cuando extendamos esta clase en el constructor llamemos al constructor padre pasándole este nombre. A continuación se muestra un ejemplo de código:

public class MiServicio extends IntentService{

    public MiServicio () {

          super "Nombre de mi servicio");

    }

    @Override

    protected void onHandleIntent(Intent intencion) {

           ...

    }

}


El siguiente método que hay que sobrescribir es onHandleIntent. Este método será lanzado cada vez que se arranque el servicio, pero en este caso se lanzará en hilo nuevo. A través del parámetro intención se podrán enviar datos en forma de extras. Es importante destacar que si se lanzan varias peticiones de servicio estas serán  puestas en una cola. Se irán atendiendo una tras otra sin que haya dos a la vez en ejecución. Este comportamiento puede ser interesante para algunas tareas, pero no para otras. Por ejemplo, si tenemos un servicio que descarga una foto de nuestro catálogo, seguramente sería más interesante hacerlo a medida que se piden y no de una en una. 

Finalmente para lanzar un IntentService hay que usar startService(). Se realiza exactamente igual que para lanzar un Service.

Ejercicio paso a paso: Un servicio en su  propio hilo 

En este ejercicio aprenderemos a crear servicios que se ejecutan en un hilo de ejecución diferente al principal utilizando la clase IntentService. Además veremos algunas limitaciones de este tipo de servicios, como la imposibilidad de acceder al sistema gráfico.

 

1.      Abre el proyecto IntentService creado en el ejercicio anterior.

2.      Crea la clase IntentServiceOperacion con el siguiente código:

public class IntentServiceOperacion extends IntentService{

   public IntentServiceOperacion() {

          super("IntentServiceOperacion");

   }

 

   @Override

   protected void onHandleIntent(Intent intent) {

          double n = intent.getExtras().getDouble("numero");

          SystemClock.sleep(5000);

          MainActivity.salida.append(n*n + "\n");       

   }

}

 

3.     Abre MainActivityy modifica la línea:

             Intent i = new Intent(this, ServicioOperacion.class);

por:

             Intent i = new Intent(this, IntentServiceOperacion.class);
 

4.     Ejecuta la aplicación. Tras pulsar el botón el resultado ha de ser:

 

5.     Abre la vista LogCat y busca el siguiente Error:

Te indica que solo desde el hilo principal se va a poder interactuar con las vistas del interfaz de usuario. También está prohibido desde otros hilos usar la clase Toast.

Como un hilo que hemos creado pertenece al mismo proceso que el hilo principal, compartimos con este todas las variables. Para devolver el valor calculado, podríamos implementar un método o variable público, tanto en la clase del servicio, como de la actividad. No obstante, vamos a resolver este problema utilizando un mecanismo más elegante, los receptores de anuncios. Se explica en el siguiente apartado.

 

 

Hasta ahora hemos visto cómo los receptores de anuncios nos permitían reaccionar ante ciertas circunstancias que ocurrían en el sistema (batería baja, llamada entrante, etc.). En este apartado vamos ver lo sencillo que resulta crear nuestros propios anuncios broadcast y recogerlos desde cualquier componente de nuestra aplicación. Además estos anuncios también podrán ser recogidos desde otras aplicaciones.

En un apartado anterior hemos visto como asociar anuncios broadcast a receptores de anuncio por medio de AndroidManifest.xml. En este apartado vamos a realizar la misma tarea utilizando Java. El programador puede escoger uno u otro modo según le convenga. 

 

Ejercicio paso a paso: Creación de un nuevo anuncio broadcast

 

En el ejercicio anterior hemos creado un servicio desde una actividad para realizar una operación matemática. Una vez que el servicio ha concluido su trabajo queríamos que avisara a la actividad y le devolviera el valor resultante. En este ejercicio realizaremos este trabajo por medio de un anuncio broadcast.

1.      Abre el proyecto IntentServicecreado en el apartado anterior.

2.      Añade el siguiente código dentro de la clase MainActivity:

public classReceptorOperacion extends BroadcastReceiver {

      public static final String ACTION_RESP=  
     "com.example.intentservice.intent.action.RESPUESTA_OPERACION"
;

      @Override

      public void onReceive(Context context, Intent intent) {

         Double res = intent.getDoubleExtra("resultado", 0.0);

         salida.append(" "+ res);

      }

   }
 

Esta nueva clase solo va a utilizarse en esta actividad por lo que puede ser definida dentro de la clase MainActivity, en lugar de en un fichero independiente. Se trata de un receptor broadcast, que cada vez que llegue un nuevo anuncio, leerá un valor enviado en el extra "resultado", y lo añadirá al TextView salida.

3.      Añade las siguientes líneas al método onCreate()

      IntentFilter filtro = new
                      IntentFilter(ReceptorOperacion.ACTION_RESP);

      filtro.addCategory(Intent.CATEGORY_DEFAULT);

      registerReceiver(new ReceptorOperacion(), filtro);
 

Con este código hemos asociado un anuncio broadcast a nuestro receptor de anuncios. Como vimos en otro apartado esta tarea también puede realizarse por medio de AndroidManifest.xml. El programador puede escoger uno u otro modo según le convenga. Al tratarse de un anuncio para una comunicación interna a nuestra aplicación, parece más conveniente realizarlo así que publicarlo por AndroidManifest.

4.      Nos queda lanzar el anuncio broadcast. Para ello añade las siguiente línea  de IntentServiceOperacion.onHandleIntent ():

MainActivity.salida.append(n*n + "\n");          

por:

      Intent i = new Intent();

      i.setAction(ReceptorOperacion.ACTION_RESP);

      i.addCategory(Intent.CATEGORY_DEFAULT);

      i.putExtra("resultado", n*n);

      sendBroadcast(i);

5.      Verifica que la aplicación funciona perfectamente. Pulsa repetidas veces el botón y verifica cómo esta no se bloquea mientras se calculan las operaciones. Advierte cómo, aunque se pulse tres veces seguidas, no comienzan las tres operaciones a la vez. Estas serán realizadas de una en una, de manera que irán apareciendo los resultados a intervalos de 5 segundos.

6.      Modifica el tiempo de retardo para que este sean 25 seg. (sleep(25000)).Ejecuta de nuevo la aplicación y observa cómo el sistema no nos muestra ningún error.