Gestion des messages broadcast¶
Le projet d’exemple pour ce chapitre
Vous pouvez télécharger le projet contenant les exemples de ce chapitre :
android-demo-app.zip
Nous avons vu que les activités sont lancées à partir de l’émission d’un intent. Un système Android généralise ce principe avec les messages de type broadcast. En informatique, la notion de broadcast désigne la télédiffusion d’une information sans connaissance a priori ni du nombre ni du type de composants qui peuvent traiter cette information.
Un système Android fournit un modèle de publication/souscription pour l’émission et la réception des messages de type broadcast. Un composant peut émettre un message tandis qu’un autre peut s’inscrire pour recevoir ce type de message. Cela permet de créer une système de communication à couplage faible : les émetteurs n’ont pas besoin de connaître les récepteurs (et réciproquement).
Le système Android lui-même émet de nombreux messages en broadcast. Par exemple, si votre application à besoin de se connecter à Internet, elle peut adapter son comportement lorsque le système perd la connexion réseau (mais également lorsque la connexion réseau est rétablie). Pour cela, vos activités peuvent souscrire à la réception de broadcast de type NETWORK_STATE_CHANGED_ACTION pour être prévenue de l’évolution de la connexion Wifi. On trouve de nombreuses utilisations pour la prise en charge de broadcast système : connaître les changements d’état du réseau, être prévenu lorsque la batterie est déchargée, savoir que le téléphone est mis en veille ou sort de veille, être prévenu du démarrage du système ou de son arrêt…
Le BroadcastReceiver¶
Le BroadcastReceiver est une classe abstraite qui permet de créer un composant en charge de recevoir des broadcasts. Une classe héritant de BroadcastReceiver doit fournir une implémentation de la méthode abstraite onReceive pour traiter le message.
On peut ensuite déclarer le BroadcastReceiver dans le manifeste de l’application pour l’associer à un filtre d’intent, c’est-à-dire pour définir les messages auxquels le récepteur s’inscrit.
Suivre le branchement sur secteur¶
Prenons l’exemple d’une application qui souhaite être prévenue lorsque le téléphone est branché sur secteur ou bien lorsqu’il passe en mode batterie. Ces changements d’états correspondent à deux broadcasts différents :
Pour le branchement sur secteur, l’action est
android.intent.action.ACTION_POWER_CONNECTED
qui est donnée par la constante ACTION_POWER_CONNECTEDPour le passage sur batterie, l’action est
android.intent.action.ACTION_POWER_DISCONNECTED
qui est donnée par la constante ACTION_POWER_DISCONNECTED
On peut créer un BroadcastReceiver pour traiter ces deux actions :
package dev.gayerie.monappli;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class PowerReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_POWER_CONNECTED)) {
Toast.makeText(context, "Connecté sur secteur", Toast.LENGTH_LONG).show();
} else if (intent.getAction().equals(Intent.ACTION_POWER_DISCONNECTED)) {
Toast.makeText(context, "En mode batterie", Toast.LENGTH_LONG).show();
}
}
}
Dans cet exemple simple, l’implementation de PowerReceiver
se contente de
vérifier l’action correspondant à l’intent reçu en paramètre de la méthode
onReceive pour afficher une information à l’utilisateur.
Enfin, pour que le BroadcastReceiver reçoive les messages, il faut le déclarer
dans le manifeste de l’application dans la balise <application>
:
<receiver android:name="dev.gayerie.monappli.PowerReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
</intent-filter>
</receiver>
En déclarant le BroadcastReceiver dans le manifeste de l’application, il n’est
pas nécessaire que l’application soit démarrée pour que le message soit reçu.
Dans ce cas, le système démarre l’application sans aucune activité et se contente
d’instancier un objet de la classe PowerReceiver
pour appeler sa méthode
onReceive. Le premier paramètre de cette méthode correspond au contexte d’exécution
du BroadcastReceiver. Il est possible de s’en servir pour appeler la méthode
startActivity. Un BroadcastReceiver peut donc effectuer des actions complexes
en réponse à un message.
Suivre l’état de la connexion Wifi¶
Pour une application dépendante de la connexion Internet, il peut être
intéressant de réagir au changement d’état de cette connexion. On peut déclarer
une BroadcastReceiver pour l’action android.net.wifi.STATE_CHANGE
qui est
également fournie par la constante NETWORK_STATE_CHANGED_ACTION.
Ci-dessous un exemple d’implémentation d’un BroadcastReceiver :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package dev.gayerie.monappli;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.widget.Toast;
public class WifiReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
NetworkInfo networkInfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
if (networkInfo.isAvailable()) {
Toast.makeText(context, "Connecté au réseau", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(context, "Déconnecté du réseau", Toast.LENGTH_LONG).show();
}
}
}
}
|
Note
À la ligne 15, on récupère un objet de type NetworkInfo en appelant la méthode getParcelableExtra. En effet, un intent peut contenir comme extra des informations complexes (pas uniquement des primitives ou des chaînes de caractères). Pour Android, on parle d’objets parcelables. Il s’agit d’objets qui peuvent être sérialisés de manière à pouvoir être transmis d’une application à l’autre.
On déclare le BroadcastReceiver dans le manifeste de l’application dans la balise
<application>
:
<receiver android:name="dev.gayerie.monappli.WifiReceiver">
<intent-filter>
<action android:name="android.net.wifi.STATE_CHANGE"/>
</intent-filter>
</receiver>
Recevoir des broadcasts depuis une activité¶
La déclaration d’un BroadcastReceiver dans le manifeste de l’application implique que, si nécessaire, l’application sera démarrée (sans activité) et un BroadcastReceiver sera instancié pour traiter le message. Ce n’est pas toujours ce que l’on désire. Imaginons qu’une activité a besoin d’adapter son comportement lorsque le système est ou non en mode hors-connexion (le mode avion). Elle peut consulter cet état mais elle a aussi besoin de savoir si cet état change. Dans ce cas, l’activité a besoin d’un BroadcastReceiver mais uniquement quand elle est affichée. On utilise la méthode registerReceiver pour associer une activité à un BroadcastReceiver. Attention, il ne faut surtout pas oublier d’appeler la méthode unregisterReceiver quand l’activité n’a plus besoin du BroadcastReceiver.
Pour illustrer notre exemple, voici le code source de l’activité.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | package dev.gayerie.monappli;
import androidx.appcompat.app.AppCompatActivity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.provider.Settings;
import android.widget.TextView;
public class AirplaneActivity extends AppCompatActivity {
private TextView airplaneModeView;
private AirplaneModeReceiver airplaneModeReceiver = new AirplaneModeReceiver();
private class AirplaneModeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
setAirplaneModeMessage();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.getApplication().getApplicationContext();
setContentView(R.layout.activity_airplane);
airplaneModeView = findViewById(R.id.airplaneMode);
}
@Override
protected void onResume() {
super.onResume();
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
registerReceiver(airplaneModeReceiver, intentFilter);
setAirplaneModeMessage();
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(airplaneModeReceiver);
}
private void setAirplaneModeMessage() {
if(airplaneModeView != null) {
airplaneModeView.setText(isAirplaneMode() ? "Mode avion": "Pas mode avion");
}
}
private boolean isAirplaneMode() {
return Settings.System.getInt(this.getContentResolver(),
Settings.System.AIRPLANE_MODE_ON,
0) != 0;
}
}
|
Dans la classe AirplaneActivity
, on déclare une classe interne
AirplaneModeReceiver
qui est un BroadcastReceiver (lignes 18 à 23). On
dispose de la méthode setAirplaneModeMessage
(ligne 47 à 51) qui permet de
mettre à jour l’interface graphique avec un message selon l’état du mode avion.
Dans l’activité, on redéfinit la méthode onResume pour mettre à jour l’interface
graphique mais également pour enregistrer le BroadcastReceiver (lignes 36-37)
en l’associant à un IntentFilter. On redéfinit également la méthode onPause
pour être sûr de désenregistrer le BroadcastReceiver (ligne 44) lorsqu’il n’est
plus utile à l’activité.
Important
Les méthodes onResume et onPause sont très utiles dans une activité pour activer/désactiver des ressources systèmes comme un BroadcastReceiver.
Émettre un message broadcast¶
Une application peut elle-même émettre un message broadcast en utilisant la méthode de contexte sendBroadcast. Le message est représenté sous la forme d’un Intent. Il est donc possible de concevoir une application dans laquelle les différents composants peuvent s’échanger des informations sur le modèle de la publication/souscription.
Intent intent = new Intent();
intent.setAction("dev.gayerie.monappli.MON_MESSAGE");
// on ajoute éventuellement des extras
intent.putExtra("info","Ceci est une info accompagnant le message");
// émission du message
this.sendBroadcast(intent);
Lorsque les messages émis sont à destination uniquement de composants de notre application, il est possible d’utiliser le LocalBroadcastManager qui permet de limiter la diffusion de messages à l’application elle-même.
Intent intent = new Intent();
intent.setAction("dev.gayerie.monappli.MON_MESSAGE");
// on ajoute éventuellement des extras
intent.putExtra("info","Ceci est une info accompagnant le message");
// émission locale à l'application
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
Note
Dans le manifeste de l’application, pour limiter un BroadcastReceiver à la
réception de messages locaux, on utilise l’attribut android:exported
avec
la valeur false
dans la déclaration.
<receiver android:name="dev.gayerie.monappli.MyLocalReceiver"
android:exported="false">
<intent-filter>
<action android:name="dev.gayerie.monappli.MON_MESSAGE"/>
</intent-filter>
</receiver>