Receiver, CGM et +1
Android Publish/Subscribe,
GCM, Google Cloud Messaging : une introduction
Notes de cours
jean-michel Douin, douin au cnam point frversion : 23 Avril 2013
Receiver, CGM et +2
Sommaire
• Le socle, les fondements– Le patron publish-subscribe
• L’entité Android de base : Receiver– Publication
• sendBroadcast(Intent i) , sendOrderedBroadcast
– Souscription• onReceive, (BroadcastReceiver)
– Exemples: • Abonnement à un évènement
– Batterie, réception de SMS• Abonnement à une alarme
– Service déjà installé sur le mobile
• GCM, Google Cloud Messaging– Google/Cloud s’occupe de tout
• Compte gmail dédié• Abonnement de plusieurs mobiles• Librairies, BroadcastReceiver et service, prêts à l’emploi
– Ce qui reste à faire: Une classe dérivée + un appel de méthodes, c’est tout
Receiver, CGM et +3
Principale bibliographie
• Le tutorial indispensable– http://developer.android.com/google/gcm/index.html
• Avant il y avait c2dm– Vogella
• http://www.vogella.com/articles/AndroidCloudToDeviceMessaging/article.html
– http://blog.octo.com/notifications-push-android-c2dm/– https://github.com/joemoore/c2demo
Receiver, CGM et +4
Les fondements
• Le patron publish/subscribe
– Publication à l’occurrence d’un évènement
– Un sms entrant, la batterie déchargée, …– Un évènement utilisateur
– Souscription à un évènement
– Agir en conséquence
– Un médiateur se charge• De la réception des évènements, de leur filtrage• De la diffusion auprès des souscripteurs sélectionnés
Receiver, CGM et +5
Publish-Subscribe,cf NSY102
• source: http://www2.lifl.fr/icar/Chapters/Intro/intro.html
Receiver, CGM et +6
Publish-subscribe/ pull-push
• Les news d’internet, ou le forum de jfod …– Enregistrement d’un « client » à un sujet de discussion,– Un des « clients » décide de poster un message,– Les utilisateurs à leur initiative vont chercher l’information,
– Publish-subscribe, mode pull
• Les listes de diffusion, logiciels de causerie, (« chat »)– Abonnement d’un « client » à une liste de diffusion,– Un des « clients » décide de poster un message,– Tous les abonnés reçoivent ce message,
– Publish-subscribe, mode push
Receiver, CGM et +7
Un exemple, mode push
• Source: http://lmi92.cnam.fr/NSY102/annales/2008/
mediator : Mediatorp1 : Publishers1 : Subscriber
s2 : Subscriberp2 : Publisher
addSubscriber(s1,meteo)
publish(p1,meteo,"sun")
update(p1,meteo,"sun")
addSubscriber(s2,sensor)
publish(p2,sensor,"25°")
update(p1,meteo,"rain")
publish(p1,meteo,"rain")
publish(p1,meteo,"sun")
update(p1,meteo,"sun")
publish(p2,sensor,"27°")
update(p2,sensor,"27°")
addSubscriber(s2,meteo)
publish(p1,meteo,"sun")
update(p1,meteo,"sun")
update(p1,meteo,"sun")
Receiver, CGM et +8
En Java/ notation UML/BlueJ
• Source: http://lmi92.cnam.fr/NSY102/annales/2008/
Receiver, CGM et +9
Android et le patron Publish/Subscribe
• Une application Android peut :
– Souscrire à un thème de publication, • réception de SMS, niveau de batterie, …
– Publier un évènement • une alarme à son échéance, un évènement interne à l’application, …
– Google Cloud Messaging une API prête à l’emploi• plusieurs mobiles peuvent souscrire et être notifiés « Over the Air »
Receiver, CGM et +10
Android/Publish-Subscribe : les bases
• Source : http://marakana.com/s/architecting_android_apps,1178/index.html
souscription publication
Receiver, CGM et +11
Publish-Subscribe/Intent & Context
• http://www2.lifl.fr/icar/Chapters/Intro/intro.html
Context
X,Y Intent
Receiver
Receiver, CGM et +12
Souscription : schéma de programme
• Basée sur les– Intent (Topic), – Context (Mediator), – BroadcastReceiver (Subscriber).
import android.content.Intent;import android.content.Context; import android.content.BroadcastReceiver;
import android.util.Log;
public class ReceiverTemplate extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) { Log.i(TAG, "onReceive action: "+intent.getAction() ); }
}
Receiver, CGM et +13
Souscription effective
• Déclarative, AndroidManifest.xml– Au sein d’une application
<receiver android:name=".ReceiverTemplate">
<intent-filter>
<action android:name=" android.intent.action.SMS_RECEIVED_ACTION " />
</intent-filter>
</receiver>
• Par programme– getApplicationContext().registerReceiver(
new ReceiverTemplate(),
new IntentFilter(Intent.SMS_RECEIVED_ACTION));
Receiver, CGM et +14
Les acteurs
• classe Context, le Mediator– http://developer.android.com/reference/android/content/Context.html
• classe BroadcastReceiver, le Subscriber– http://developer.android.com/reference/android/content/BroadcastReceiver.html
• classe Intent + IntentFilter, X,Y les thèmes– http://developer.android.com/reference/android/content/Intent.html
• À suivre: deux exemples avec le système Android – Le niveau de la batterie vient d’être réactualisé,– Un sms vient d’arriver
Receiver, CGM et +15
Exemple 1 : la batterie a changé d’état
public class LowBatteryActivity extends Activity {
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main);
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
// Souscription registerReceiver(new BatteryChangedReceiver(), filter); }
// le Souscripteur private static class BatteryChangedReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) { Toast.makeText(context, "battery changed", Toast.LENGTH_SHORT).show(); Log.d(TAG, "onReceive action: "+intent.getAction() ); } }}• http://developer.android.com/training/monitoring-device-state/battery-monitoring.html
Receiver, CGM et +16
La publication par Android ressemblerait à
• android.os.BatteryManager
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("ACTION_BATTERY_CHANGED");
broadcastIntent.putExtra("level", 3567);
//
// http://developer.android.com/reference/android/os/BatteryManager.html //
context.sendBroadcast(broadcastIntent);
Receiver, CGM et +17
Exemple 2 : réception d’un SMS
// le souscripteur
public class SMSReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) { Toast.makeText(context, "sms received", Toast.LENGTH_SHORT).show();
}
}
Receiver, CGM et +18
Configuration du souscripteur, <receiver />
// AndroidManifest.xml
// La souscription<receiver android:name=".SMSReceiver">
<intent-filter>
<action android:name="android.intent.action.SMS_RECEIVED_ACTION"/>
</intent-filter>
</receiver>
Souscription effectuée (appel de registerReceiver) au chargement de l’application
Receiver, CGM et +19
Démonstration
• Les deux exemples
Intent.ACTION_BATTERY_CHANGED
android.intent.action.SMS_RECEIVED_ACTION
Receiver, CGM et +20
Autre façon de souscrire PendingIntent
• Intent à effet immédiat• PendingIntent à effet retardé
– Destiné à un tiers
• A destination d’un service existant– AlarmManager, NotificationManager …
• Souscription auprès du service– Exécution de l’intent passé en paramètre, à l’aide d’un PendingIntent
Receiver, CGM et +21
Souscription auprès d’un service existant
• Intent intent = new Intent(this, ReceiverTemplate.class);
• PendingIntent appIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
Calendar calendar = Calendar.getInstance();calendar.setTimeInMillis(System.currentTimeMillis());calendar.add(Calendar.SECOND, 3);
AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
am.set(AlarmManager.RTC, calendar.getTimeInMillis(), appIntent);
// sendBroadcast(intent) par le service d’alarme
Receiver, CGM et +22
Variante de l’écriture précédente
PendingIntent appIntent = PendingIntent.getBroadcast(this, 0, new Intent(), 0);
Calendar calendar = Calendar.getInstance();
// idem diapositive précédente
am.set(AlarmManager.RTC, calendar.getTimeInMillis(), appIntent);
// Ce sont les receveurs déclarés dans AndroidManifest.xml
// qui seront déclenchés, (méthode onReceive)
// puis sendBroadcast(intent) par le service d’alarme
Receiver, CGM et +23
Notification
• Souvent associée à la réception d’un évènement
– Déclenchée par le souscripteur• Un « Receiver », un service, …
– Exemple:• Une notification à la réception d’un message sms
» telnet localhost 5554» send 1234 SMS test message
Receiver, CGM et +24
Notification : affichage
private static void generateNotification(Context context, String message) { long when = System.currentTimeMillis();
NotificationManager notificationManager = null;notificationManager(NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);Notification notification = new Notification(R.drawable.ic_launcher,message, when);
String title = context.getString(R.string.app_name);
Intent intent = // page suivante
notification.setLatestEventInfo(context, title, message, intent);notification.flags |= Notification.FLAG_AUTO_CANCEL;notificationManager.notify(0, notification);}
Receiver, CGM et +25
Autre exemple : une notification
• Au clic sur la notification une application est déclenchée
private static void generateNotification(Context context, String message) { long when = System.currentTimeMillis();
NotificationManager notificationManager = null;notificationManager(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);Notification notification = new Notification(R.drawable.ic_launcher,message, when);String title = context.getString(R.string.app_name);
Intent notificationIntent = new Intent(context,GCMClientActivity.class);
// afin que l’intent retardée démarre une nouvelle activiténotificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent intent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
notification.setLatestEventInfo(context, title, message, intent);notification.flags |= Notification.FLAG_AUTO_CANCEL;notificationManager.notify(0, notification);}
• Source: http://android.amolgupta.in/2012/07/google-cloud-messaging-gcm-tutorial.html
Receiver, CGM et +26
Autre usage : une notification, sans effet au clic
• Au clic sur la notification rien ne se passe
private static void generateNotification(Context context, String message) { long when = System.currentTimeMillis();
NotificationManager notificationManager = null;notificationManager(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);Notification notification = new Notification(R.drawable.ic_launcher,message, when);
String title = context.getString(R.string.app_name);
PendingIntent intent = PendingIntent.getActivity(context, 0, null, 0);
notification.setLatestEventInfo(context, title, message, intent);notification.flags |= Notification.FLAG_AUTO_CANCEL;notificationManager.notify(0, notification);}
• Source: http://android.amolgupta.in/2012/07/google-cloud-messaging-gcm-tutorial.html•
Receiver, CGM et +27
Démonstration
Receiver, CGM et +28
GCM
• Google Cloud Messaging– Anciennement c2dm, Cloud to Delivering Message
• >= Android 2.2
• Message <= 4ko
• Trafic illimité– Nécessite une inscription auprès de Google
• Avec de préférence un compte gmail que l’on dédiera à cet usage
Receiver, CGM et +29
Architecture : Objectifs
• Un client, tout système connecté – S’adresse au serveur Google qui se chargera de publier auprès des mobiles
• Mobiles ayant préalablement souscrits
• Mise en œuvre : une librairie toute prête, un appel de méthode suffit
Receiver, CGM et +30
Architecture : Mise en œuvre, inscription
• Chaque participant– Doit avoir un compte google gmail– S’inscrit auprès de GCM, en retour un identifiant lui est attribué
• Un serveur mémorise, l’identifiant retourné par GCM– Un serveur au protocole HTTP de préférence– Ces serveur contient les abonnés
1)inscription
2)identifiant
3) dépôt de l’identifiant
GCM
Receiver, CGM et +31
Architecture : Mise en œuvre, inscriptions
• Le serveur contient une liste des identifiants• Un identifiant par application
– Application : un service sous Android
• Exemple : Une liste d’identifiants accessible depuis le web• http://jfod.cnam.fr/registration/demo/?cmd=toString
• id1• id2• id3• …• …• …
Receiver, CGM et +32
Architecture : Mise en œuvre, publications
• Publication par tout système connecté1,2) Obtention de la liste des identifiants, des abonnés
3) Envoi de cette liste au serveur Google/GCM accompagnée du message à transmettreGCM se charge de publier le message, de le ré-émettre, de le conserver …
2) [id1, id2, id3, ……]
1) Demande de la liste
3) Demande de publication [id1, id2, id3, ……] + message id1
message
message
message
id2
id3
Receiver, CGM et +33
Architecture : publications
• Chaque mobile ayant préalablement souscrit de la liste est notifié
– C’est un « Receiver » prêt à l’emploi qui est déclenché sur chaque mobile
– Un service est créé afin de prendre en compte cette notfication• Une de ses méthodes est appelée (onMessage)
id1message
message
message
id2
id3
Receiver, CGM et +34
Le tutorial que l’on se doit de lire
• http://developer.android.com/google/gcm/index.html
Receiver, CGM et +35
Comment ?
Création d’un projet auprès des serveurs Google1. https://code.google.com/apis/console2. En retour : un numéro_de_projet (ProjectId)
1. https://code.google.com/apis/console/#project:numéro_de_projet3. Une API key est nécessaire
1. Exemple ProjectId:138387916323, API_KEY: AIzaSyC60sIgG-fB3JSW47X2zGrOqJHGzV1jh0g
1. Un mobile s’inscrit auprès du serveur Google (API_KEY, ProjectId)1. En retour un jeton d’identification lui est attribué
• Chaque participant doit posséder un compte gmail
2. Cet identifiant est déposé sur un serveur, connu de tous les participants
2. Envoi d’un message aux participants1. Obtention de la liste des identifiants auprès du serveur
2. Publication du message
Librairies toutes prêtes
Receiver, CGM et +36
Création du projet
• https://code.google.com/apis/console/#project:138387916323
API_Key
ProjectId
Receiver, CGM et +37
Inscription, mise en oeuvre
• Depuis une activity– 1) Inscription auprès du cloud
String regId = GCMRegistrar.getRegistrationId(this);if ("".equals(regId)) { GCMRegistrar.register(this, "138387916323");
• Démarrage du service…• Attente asynchrone de la réponse de GCM
1)inscription
2)identifiant
3) dépôt de l’identifiant
GCM
Receiver, CGM et +38
Le service hérite d’une classe toute prête
public class GCMIntentService
extends GCMBaseIntentService {
– Méthode déclenchée à la suite de lademande d’inscription au GCM– 2) onRegistered(Context context, String identifiant)
• 3) Dépôt de l’identifiant sur un serveur
– onMessage(Context context, Intent intent)– onError ….– onUnregistered
1)inscription
2)identifiant
3) dépôt de l’identifiant
GCM
Receiver, CGM et +39
Liste des indentifiants
• Service web, servlette, php– http://jfod.cnam.fr/registration/demo/?cmd=toString
1)inscription
2)identifiant
3) dépôt de l’identifiant
GCM• id1• id2• id3• …• …• …
• id1• id2• id3• …• …• …
Receiver, CGM et +40
Publication
• Envoi de la liste des identifiants au GCM + un message + API_KEY• Et se charge de tout
id1message
message
message id3
• id1• id2• id3• …• …• …
API_KEY+
Message+ id1
message
message
message
id2
id3
Receiver, CGM et +41
Librairie toute prête
• \android-sdk\extras\google\gcm\gcm-client\dist\– gcm.jar
• Souscription, abonnement– Un service qu’il suffit de dériver
• public class GCMIntentService extends GCMBaseIntentService {
• Réception d’un message : un Receiver + un service– Un Receiver prêt à l’emploi<uses-permission
android:name="com.google.android.c2dm.permission.RECEIVE" /> Une classe IntentBaseService qu’il suffit de dériver <uses-permission
android:name="com.google.android.c2dm.permission.RECEIVE" />
Receiver, CGM et +42
Démonstration, Obtention du jeton
GCMRegistrar: Classe interface avec le service GCMProjectId: 1383879163231. GCMRegistrar.getRegistrationId(this, "138387916323");
• Une demande est faite auprès de Google
2. onRegistered(Context context, String regId) – Au retour de Google, la méthode du service est déclenchée
• Le l’identifiant reçu par le mobile:APA91bGy-GWQqqrgyFc652jghpTPygBsKyDHNFPWqzJDCMvtcIx7rtVecmxAYkDItfgztJcEJhyARnx0fmmp6FtqrFKNXqftmGXhRIGM5eObyLY67YkT_Aez3RJeOZFVkOpjv-_FRNASI0ZJL6VQk95Y925MQ9Wihw
Exemples ici : http://jfod.cnam.fr/registration/demo/?cmd=toString
Receiver, CGM et +43
Souscription suite
• Souscription du mobile
– Permissions• <uses-permission
android:name="com.google.android.c2dm.permission.REGISTRATION" /> • <uses-permission android:name="android.permission.INTERNET" />
– Le receveur, un extrait de AndroiManifest.xml
– <receiver
android:name=".C2DMRegistrationReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter >
<action android:name="com.google.android.c2dm.intent.REGISTRATION" >
</action>
</intent-filter>
</receiver>
Receiver, CGM et +44
Le service
public class GCMIntentService extends GCMBaseIntentService
– onRegistered(Context context, String regId) • Déclenchée par le Receiver pré-installé à la suite de l’abonnement–Mémorisation de regId
– onMessage(Context context, Intent intent)• Déclenchée par le Receiver pré-installé à la suite de la réception d’un message–String msg = intent.get("message");
Receiver, CGM et +45
Publication : le message
• Par tout système connecté,• \android-sdk-new\extras\google\gcm\gcm-server\dist\
– gcm-server.jar
Sender sender = new Sender(API_KEY);
String msg = "un message";
Message message = new Message.Builder()
//.collapseKey("1")
.timeToLive(60*60*24) // 24 heures
//.timeToLive(0) // maintenant ou jamais
.delayWhileIdle(true)
.addData("message",msg)
.build();
Receiver, CGM et +46
Publication : Envoi de message
• Publication d’un message
– Send• devices : une liste de regId
– MulticastResult result = sender.send(message, devices, 5); • // 5 essais en cas d’indisponibilité des serveurs google
– Ensuite le cloud s’occupe de tout …
Receiver, CGM et +47
Démonstration
• Démonstration
• Installez cet apk sur votre mobile …
– http://jfod.cnam.fr/cgm/GCM_Client_Demo.apk• le souscripteur/publieur
– Les sources http://jfod.cnam.fr/cgm/GCM_Client_Demo.zip
Receiver, CGM et +48
Démonstration
– Service web• Interrogations
– La liste des abonnés
» http://jfod.cnam.fr/registration/demo/?cmd=toString» http://jfod.cnam.fr/registration/demo/?cmd=list
– Un journal des notifications
» http://jfod.cnam.fr/registration/demo.log.html
• Publication– http://jfod.cnam.fr/registration/demo/?cmd=send&message=hello&source=url&valeur=-1
• Un formulaire– http://jfod.cnam.fr/cgm/demo.html
• id1• id2• id3• …• …• …
Receiver, CGM et +49
http://jfod.cnam.fr/cgm/demo.html
Receiver, CGM et +50
Mise en œuvre
• Répertoire du projet eclipse du client souscripteurDepuis l’explorateur windows, (eclipse n’est pas démarré)
créer un répertoire libs dans lequel vous copiez gcm.jarlibs>copy D:\android-sdk\extras\google\gcm\gcm-client\dist\gcm.jar .
• Répertoire du projet eclipse du client « publieur »Depuis l’explorateur windows, (eclipse n’est pas démarré)
créer un répertoire libs dans lequel vous copiez gcm-server.jarlibs>copy D:\android-sdk\extras\google\gcm\gcm-server\dist\gcm-server.jar .
. Si bluej, installez gcm-server.jar dans le répertoire +libs
A l’exécution json est requisD:\android-sdk\extras\google\gcm\gcm-server\lib\json_simple-1.1.jar
Receiver, CGM et +51
Conclusion
• Simple
• Push/polling (voir en annexe) …
• Indispensable
• Note: Sous eclipse dans le projet créer un dossier libs– Dans lequel sont copiés :
• gcm.jar pour souscrire• gcm-server.jar et json-simple-1.1.jar pour publier
Receiver, CGM et +52
Annexe consommation en « polling »
Top Related