DevCorner: uno sguardo in profondità ai BroadcastReceiver

Roberto Orgiu
Roberto Orgiu
DevCorner: uno sguardo in profondità ai BroadcastReceiver

Quello di oggi è un DevCorner che può essere definito anomalo, data la scarsa quantità di codice che ci richiederà, ma credo sia interessante dare uno sguardo più in profondità ad uno dei componenti caratteristici che il Software Development Kit di Android ci mette a disposizione, ovvero il BroadcastReceiver.

Ne abbiamo già parlato agli albori di questa rubrica, quando ancora non era nemmeno un'idea il nome stesso della serie, e sappiamo già che esistono due modi di dichiarare un Receiver, via Java o via XML: esaminiamo quindi le due alternative, ricordandoci che, in sostanza, entrambe hanno bisogno di una classe che implementa il nostro ricevitore (che chiameremo MyReceiver) e di almeno un filtro, che ci permetterà di ascoltare solamente alcuni Intent (supponiamo di voler quindi attendere l'Action "MY_PERSONAL_INTENT").

In Java avremo una situazione simile alla seguente

IntentFilter filter = new IntentFilter("MY_PERSONAL_INTENT");
registerReceiver((new MyReceiver()),filter);

Mentre in XML sarà più verosimilmente

<receiver android:name="MyReceiver">
<intent-filter>
<action android:name="MY_PERSONAL_INTENT" />
</intent-filter>
</receiver>

LEGGI ANCHE: Introduzione ai BroadcastReceiver

Detto questo, possiamo passare alle cose che magari non ci sono proprio familiari, iniziando dal ciclo di vita del nostro Receiver: per sua natura, il nostro componente vivrà finché non verrà eseguita l'ultima istruzione del suo metodo onReceive. Cosa implica questo? Per iniziare, non è permesso utilizzare richieste asincrone di alcun tipo, perché potrebbe non esserci (anzi, è molto probabile che sia così) nessuno a gestire il risultato generato dall'operazione. Inoltre, il nostro BroadcastReceiver ha un tempo massimo di 10 secondi per eseguire tutto il suo codice, prima di essere candidato alla terminazione per liberare le risorse: come non è possibile utilizzare le richieste di cui parlavamo poche righe sopra, allo stesso modo non è possibile effettuare il bind di un servizio al nostro componente, ma è buona pratica effettuare le operazioni lunghe in un servizio avviato normalmente tramite le API di Context

Context.startService()

Nello specifico, essendo il processo del nostro BroadcastReceiver considerato come foreground durante l'esecuzione dell'ormai famoso metodo onReceive, non dovrebbe mai essere terminato durante l'esecuzione dei comandi in esso contenuti, a meno che il nostro dispositivo non sia in estremo bisogno di risorse: una volta terminata l'esecuzione del codice di ricezione, il nostro componente non verrà più considerato importante e tornerà nelle fila delle normali applicazioni.

Una volta spiegato il ciclo di vita del nostro ricevitore, possiamo dare uno sguardo analitico alla sicurezza dei BroadcastReceiver: per sua natura, qualunque applicativo può lanciare un Intent che verrà catturato dal nostro componente. Per evitare che ciò avvenga, e che quindi solamente le classi della nostra app possano interagire con questo codice, possiamo agire in diverse maniere:

  • evitare di esportare il nostro ricevitore, dichiarandolo nel Manifest tramite l'attributo exported impostato a false
  • impostare un permesso nella dichiarazione XML o nella registrazione via Java, in modo tale che solamente i broadcaster che richiedono la suddetta permission possano inviare segnali al nostro ricevitore
  • modificare il nostro Receiver in modo da utilizzare solamente messaggi locali.

Un'ultima considerazione che vale certamente la pena di accennare è la differenza tra i BroadcastReceiver che possiamo definire dinamici (ovvero dichiarati tramite codice Java) e quelli che invece rimangono più statici, dichiarati nel Manifest: per diverse informazioni che non vanno consumate all'utilizzo, ma devono poter raggiungere tutti i ricevitori in ascolto in ogni momento, Android usa degli Intent sticky, i cui filtri non possono essere dichiarati nel Manifest, ma solamente tramite l'apposito costrutto Java, con l'ulteriore limite di dover dichiarare alcuni permessi per poter correttamente utilizzarne gli Extra.

Un esempio di questo comportamento è relativo alla batteria, i cui valori possono essere letti addirittura senza dover dichiarare esplicitamente un receiver, ma semplicemente estraendo l'Intent.

Anche per questa settimana siamo arrivati alla fine del nostro DevCorner e vi diamo appuntamento al prossimo martedì per nuovi articoli sullo sviluppo su Android.