A tortas con los BroadcastReceiver de Android

Un BroadcastReceiver es un componente de Android que una vez registrado reacciona cuando el sistema envía los Intent para los que estaba preparado. Un caso muy típico es el del Receiver que se registra para que reaccione cada vez que se recibe una llamada de teléfono o un SMS. Ese BroadcastReceiver capta el Intent y en un método onReceive hace lo que tenga que hacer. Se supone que es muy fácil y todo muy bonito.

Para Android tienes infinidad de ejemplos colgados por la red. Lo que nunca puedes saber seguro es si la gente que cuelga las ristras de código realmente los ha probado o no. O lo que es más probable, a ellos su código les ha funcionado y a ti no, y puede ser tan simple como que hayan omitido algún detalle de configuración, alguna opción perdida en un fichero xml, etc... Así que en mi caso voy a mostrar el código y comentar lo que he observado en un dispositivo real (4.1) y en uno emulado (2.2).

Hechos comprobados: como registrar un BroadcastReceiver

Se supone que puedes registrar el Receiver a través de unas líneas en el Manifest o por programación. La gente por ahí pone como que con declarar en un Manifest el receiver ya es suficiente y que con eso ya se registra el BroadcastReceiver. ¡Pues NO! En mi caso me ha ocurrido como los Services, sin un Activity no he podido activar el BroadcastReceiver. Otros detalles interesantes son el hacer un registerReceiver y también un unregisterReceiver para que el Receiver no salte una excepción y no le afecte el pulsar Home o Back en el activity que ha cargado el Receiver.

Hechos comprobados: el BroadcastReceiver queda registrado y responde para unos Intents sí y para otros no

En mi caso he registrado un Receiver para varios intents: el arranque del sistema, una llamada entrante, meter/sacar auriculares... Y he podido comprobar como:

  • Con el activity en primer plano, el Receiver responde bien ante el evento de los auriculares, si no NO.
  • Con el activity en primer plano o no, el Receiver sigue respondiendo al intent de llamada entrante
  • Con el reinicio del móvil, el Receiver ha respondido al intent de reinicio, y también al de llamada entrante, así que aparentemente el Receiver para esos eventos se ha quedado registrado correctamente.
Me choca un poco que para unos sí y para otros no, claro que igual es cosa del móvil, de mi configuración, vete a saber... Si vamos al manual oficial, en la sección lifecycle insisten es que tras ser atendido un Intent en el Receiver en el método onReceive() este no es que se pare, Android se supone que lo extermina:
"then upon returning from onReceive() the system will consider its process to be empty and aggressively kill it so that resources are available for other more important processes."
Ver manual oficial
Por cierto, también advierte sobre el tipo de tareas que se pueden hacer en un Receiver y dice que nada de tareas asíncronas. Lo que sugiere para escenarios de ejecuciones más prolongadas es una combinación de Service con un Receiver (Oh my god).

Supongo que una cosa es que se carga la instancia de nuestro BroadcastReceiver, pero lo que he podido comprobar es que el Receiver sigue registrado y en caso de surgir un intent de los que espera entonces se Android lo instancia. Reitero, luego la instancia muere, pero el receiver sigue registrado, es decir: No está muerto lo que yace eternamente... ;)

Nuestro BroadcastReceiver

Bueno, pues hacer un BroadcastReceiver en principio es una tontería, basta con extender una clase y procurar sobrescribir el método onReceive. En nuestro caso, como antedemos a más de un Intent he tenido que meter varios if (un switch con Strings no le gustaba en mi versión de jdk, será la 1.6).

package info.pello.android.broadcastreceiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.util.Log;

/**
 * Sample of BroadcastReceiver, every time the phone get a phone
 * this Receiver should be called. It'll just log a message but It
 * could save info in a database, to a file, somewhere on the cloud...
 * @author Pello Xabier Altadill Izura
 * @greetz Remírez
 */
public class PhoneCallReceiver extends BroadcastReceiver {
	
	/**
	 * default constructor
	 */
	public PhoneCallReceiver() {
		Log.d("PELLODEBUG","BR> Instance creaed");
	}

	/**
	 * this handler will be fired when w receive an Intent for us
	 */
	@Override
	public void onReceive(Context context, Intent intent) {
		 Bundle intentExtras = intent.getExtras();
		 
		// is intent a REBOOT?
		if (intent.getAction().equals(Intent.ACTION_REBOOT)) {
			Log.d("PELLODEBUG","BR> Received intent: system rebooted");
		}

		// or maybe a headphone plug/unplug?
		if (intent.getAction().equals("android.intent.action.HEADSET_PLUG")) {
			Log.d("PELLODEBUG","BR> Received intent: headsetPlugged");
		}
		
		// or maybe phone is ringing?
		if (intent.getAction().equals("android.intent.action.PHONE_STATE")) {
			Log.d("PELLODEBUG","BR> Received intent: RING RING!!");
			// Now we extract number from intent extras

	
		    if (null != intentExtras) {
			      String state = intentExtras.getString(TelephonyManager.EXTRA_STATE);
			      if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
			        String callingNumber = intentExtras.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);

					saveInformation("BR> Call received: " + state + " ," + callingNumber);
			      }
			    }
		}


		Log.d("PELLODEBUG","BR> Received intent: " + intent.getAction());

	}
	
	/**
	 * method called from onReceive to store information
	 * @param info
	 * @return
	 */
	private boolean saveInformation (String info) {
		Log.d("PELLODEBUG","BR> saving info: " + info);
		return true;
	}
	
	
}

La MainActivity

Otros dirán lo que sea pero en mi caso ha sido imprescindible para registrar el Receiver con la llamada registerReceiver().

package info.pello.android.broadcastreceiver;

import android.os.Bundle;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.IntentFilter;
import android.util.Log;

/**
 * MainActivity that simply register a BroadcastReceiver.
 * It's important to unregister on Stop event or we will get an Exception.
 * @author Pello Xabier Altadill Izura
 * @greetz For those who dare to mix Android and Threads
 */
public class MainActivity extends Activity {
	PhoneCallReceiver myPhoneCallReceiver = new PhoneCallReceiver();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Log.d("PELLODEBUG","MainActivity> unregistering Receiver");
	}
	
	@Override
	protected void onStart() {
		myPhoneCallReceiver = new PhoneCallReceiver();
		IntentFilter myFilter = new IntentFilter();
		myFilter.addAction("android.intent.action.HEADSET_PLUG");
		myFilter.addAction("android.intent.action.PHONE_STATE");
		myFilter.addAction("android.intent.action.BOOT_COMPLETED");
		myFilter.addAction("android.intent.action.QUICKBOOT_POWERON");

		// We register the receiver...
		// and off we go...
		this.registerReceiver(myPhoneCallReceiver, myFilter);
		Log.d("PELLODEBUG","Activity> receiver registered");
		super.onStart();
	}
	
	/**
	 *  We must override this to avoid 
	 *  Are you missing a call to unRegisterReceiver()
	 *  exception	 
	 *  */
	@Override
	protected void onStop() {
		Log.d("PELLODEBUG","MainActivity> unregistering Receiver");
		if (null != myPhoneCallReceiver)
			this.unregisterReceiver(myPhoneCallReceiver);
		else
			Log.d("PELLODEBUG","MainActivity> is null already");
		super.onStop();
	}
	

}


It's alive! It's aliveeee! El logcat mostrando los Intents del receiver
El Manifest

El proyecto, lo diré por si no era evidente, es un proyecto NORMAL, con su activity por defecto NORMAL. Se le han añadido un par de permisos, uno para poder ver el estado del teléfono (para los intents de llamadas) y otro para que se entere cuándo hay un reinicio del sistema. Este último es muy interesante y como decía he comprobado que efectivamente al reiniciar el BroadcastReceiver responde al Intent. Luego en la propia configuración del manifest he indicado que hay un BroadcastReceiver y los Intents a los que responde.




    
    
    
    
        
            
                
                
            
        

        
            
                
                
	        	
    		    
                
        
    



Por cierto, hasta ahora estaba poniendo al revés el orden en los mensajes de Log.d, hay que poner el tag primero. La información la podemos seguir viendo pero es más cómodo para aplicar un filtro tag: en el logcat. En este proyecto ya está correcto. Sorry!

by Pello Altadill 08/21/2013 14:22:14 - 486 hits

Ejemplo de REST con Node.js + express y MongoDB

Alrededor de Node.js existen infinidad de proyectos en plena ebullición, y prueba de ello es que solamente para desarrollar webs existen varios frameworks, algunos basados en el clásico MVC y otros más orientados a servir recursos REST. Ese es el caso de Express, uno de los frameworks más populares disponibles para Node.js. En este post veremos cómo crear un servidor que ofrezca recursos REST a un frontend. Si no te gusta javascript, amijo, jejej, no sé: deja la web y vuelve al Cobol, porque esto v...

by Pello Altadill 08/20/2013 23:15:49 - 440 hits

More »

Ejemplo de Web Workers, los hilos javascript

Desde que los navegadores tenían soporte para Javascript, su código siempre se ha ejecutado en un único hilo. Los scripts que se desarrollan para el navegador están orientados a eventos y estos se van ejecutando en una especie de cola. Es decir, conforme se van generando los eventos estos van a una cola de tareas y Javascript los va procesando de uno en uno en un bucle. Web Workers, hilos en tu navegador HTML5 trae como novedad los Web Workers, que son procesos de javascript separado...

by Pello Altadill 08/20/2013 12:51:28 - 554 hits

More »

Services en Android

En Android no solamente hay aplicaciones de ventanas o activities. Hay muchos otros tipos de elementos como por ejemplo los Services. Los servicios nos permiten dejar un programa residente en la memoria y disponible para cualquiera que lo necesite. Los servicios no tienen interfaz gráfico ni nada, son programas pensados para ser utilizados desde las Activities o incluso desde otros servicios. Un service puede crearse para dar servicio -privado- a una sola aplicación o puede hacerse público (a través ...

by Pello Altadill 08/19/2013 22:29:37 - 580 hits

More »

Creando módulos en Node.js

Lo del servidor web con pocas líneas o refactorizado para que tenga forma de clase va quedando más o menos apañado. Pero en cualquier proyecto que vaya creciendo vamos a necesitar bastantes más cosas y claro, ir metiendo todo el programa en un único fichero, aunque algún programador bajeril pueda decir que es más simple, pues está muy feo. Como en cualquier otro lenguaje y sea el tipo de programación que se...

by Pello Altadill 08/18/2013 22:45:09 - 275 hits

More »

AsyncTask en Android

Los AsyncTask están recomendados para tareas concretas y finitas, como por ejemplo descargar el contenido de una URL. Si lo que necesitas es una especie de Hilo que esté ejecutándose contínuamente tendrías que usar los Thread convencionales (no se detienen ni al pulsar home ni al pulsar Back) o directamente crear un Service de Android. Para crear un AsyncTask debes extender la clase AsyncTask indicándole tres tipos de clase en la declaración, como por ejemplo AsyncTask<String, String, V...

by Pello Altadill 08/18/2013 11:06:08 - 592 hits

More »

Perfiles de desarrollo y producción con Maven

Algo muy común y recomendable en cualquier proyecto medianamente serio es contar con un servidor de desarrollo y otro de producción, además del propio equipo del programador donde se hacen las primeras pruebas. Los programas se prueban en local, se prueban en desarrollo y finalmente, cuando se ha verificado que todo está correcto, se puede pasar a producción donde se explotará una BD con datos reales y habrá usuarios reales. Hay lugares en los que incluso existe una servidor de pre-producción....

by Pello Altadill 08/17/2013 22:16:00 - 470 hits

More »

Hilos en Android con Threads java

Toda aplicación de Android tiene al menos un hilo o thread de ejecución. Ese thread es el encargado de la pantalla que tienes ante ti y es imprescindible por la manera en la que Android gestiona los cambios en el interfaz: a través de una cola de mensajes. Es decir, cada vez que aplicamos algún cambio en algún View no se aplica directamente sino que se deja la petición en una cola. Y ese hilo, el UI thread o el hilo principal de la aplicación es quien precisamente se encarga de procesar esa cola...

by Pello Altadill 08/17/2013 10:50:52 - 607 hits

More »

Servidor web de Node.js refactorizado

Sí, seguro que el primer código que has visto de Node.js es uno de esos milagrosos servidores web hechos con pocas líneas. Olvídese de aparatosos sockets, de punteros o de BufferedStreamsWriters, un módulo, unos callbacks, eliges un puerto y a dar servicio. Bueno, uno trata de ser un good Node.js citizen y este código se entiende y mola, pero a la larga, si lo queremos ir mejorando y añadiendo otras funcionalidades cada vez va a ser más complicado mantenerlo. Lo que propongo en ...

by Pello Altadill 08/16/2013 23:18:31 - 256 hits

More »