Dopo che le lezioni precedenti hanno mostrato l'impostazione di massima dell'esempio e della parte server, vedremo finalmente la vera e propria app che
riceverà e gestirà le notifiche push da parte del server.
È costituita da due componenti:
- l'Activity che si occupa della registrazione a GCM;
- il Broadcast Receiver che aspetta l'arrivo della notifica push.
Osserviamo, per prima cosa, il file AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="it.html.googleservices.push" >
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission android:name="it.html.googleservices.push.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="it.html.googleservices.push.permission.C2D_MESSAGE" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity" >
...
</activity>
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<receiver
android:name=".NotificationReceiver">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="it.html.googleservices.push" />
</intent-filter>
</receiver>
</application>
</manifest>
Notiamo che:
-
all'interno del nodo
<application>
ci sono le due componenti configurate. Il Broadcast Receiver è collegato ad un IntentFilter legato
all'azionecom.google.android.c2dm.intent.RECEIVE
, che indica la ricezione di una notifica push; -
all'interno di
<application>
c'è anche il nodo<metadata>
che specifica la versione dei Google Play Services; -
al di fuori di
<application>
ci sono le permission:INTERNET
per il collegamento in Rete,com.google.android.c2dm.permission.RECEIVE
per ricevere
le notifiche push ed infine ce n'è una personalizzata, che deve corrispondere al nome del package più il suffisso.permission.C2D_MESSAGE
. Lo scopo di
quest'ultima è impedire che altre applicazioni possano registrarsi alle notifiche push della nostra. Il nome deve necessariamente combaciare con il formato
previsto, altrimenti l'app non sarà in grado di ricevere notifiche.
Il codice che viene presentato di seguito, per funzionare, ha bisogno che di un paio di parametri:
- l'URL della pagina PHP nella costante BACKEND_URL;
- il Project Number del progetto Google in SENDER_ID.
L'Activity è composta da tre metodi:
public class MainActivity extends Activity {
// inserire l'url della pagina PHP
private static final String BACKEND_URL="http://.......";
// nella stringa SENDER_ID inserire il Project Number del proprio progetto Google
String SENDER_ID = "…...";
TextView mDisplay;
GoogleCloudMessaging gcm;
Context context;
String regid;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context=this;
mDisplay = (TextView) findViewById(R.id.display);
gcm = GoogleCloudMessaging.getInstance(this);
registerInBackground();
}
private void registerInBackground()
{
new AsyncTask<Void, Void, String>()
{
private ProgressDialog dialog=null;
protected void onPreExecute()
{
dialog=ProgressDialog.show(context, "Registrazione presso GCM", "Tentativo in corso...", true, false);
};
@Override
protected String doInBackground(Void... params)
{
String msg = "";
try {
if (gcm == null)
{
gcm = GoogleCloudMessaging.getInstance(context);
}
regid = gcm.register(SENDER_ID);
}
catch (IOException ex)
{
return null;
}
return regid;
}
@Override
protected void onPostExecute(String regid)
{
dialog.dismiss();
if (regid!=null) {
sendIDToApplication(regid);
mDisplay.setText(regid);
}
else
Toast.makeText(context,"Errore: registrazione su GCM non riuscita!",Toast.LENGTH_LONG).toString();
}
}.execute();
}
private void sendIDToApplication(String regid)
{
new AsyncTask<String, Void, Void>()
{
@Override
protected Void doInBackground(String... params)
{
String regid=params[0];
HttpClient client=new DefaultHttpClient();
HttpPost request=new HttpPost(BACKEND_URL);
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("regid", regid));
try {
request.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response=client.execute(request);
int status=response.getStatusLine().getStatusCode();
} catch (UnsupportedEncodingException e) {
return null;
} catch (ClientProtocolException e) {
return null;
} catch (IOException e) {
return null;
}
return null;
}
}.execute(regid);
}
}
Nel metodo onCreate
vengono effettuate le prime configurazioni e viene invocato il metodo registerInBackground che
effettua la registrazione del client presso GCM.
La registrazione a GCM richiede in sé pochissimo codice. Un'istanza della classe GoogleCloudMessaging invierà in remoto il SENDER ID (ovvero il Project Number del progetto Google, come già detto) e riceverà una stringa contenente il REGISTRATION ID.
In onPostExecute
, oltre alla dismissione della ProgressDialog
, viene anche invocato il metodo sendIdToApplication che si occupa
dell'inoltro del REGISTRATION ID alla pagina PHP che abbiamo visto nella lezione precedente. Questo compito viene svolto in background grazie ad
AsyncTask
, e sfrutta gli oggetti della classe HttpClient
inviando una richiesta POST
.
Si noti che il REGISTRATION ID è stato collocato in un elemento della richiesta POST
di nome regid
. Quest’ultimo deve coincidere con il nome del
parametro usato nello script PHP, spiegato nella lezione precedente.
Il BroadcastReceiver
è così costituito:
public class NotificationReceiver extends BroadcastReceiver
{
public static final int NOTIFICATION_ID = 1;
private NotificationManager mNotificationManager;
NotificationCompat.Builder builder;
public NotificationReceiver() {
}
@Override
public void onReceive(Context context, Intent intent)
{
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);
Bundle extras = intent.getExtras();
String messageType = gcm.getMessageType(intent);
if (!extras.isEmpty())
{
if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType))
{
// emette una notifica sul dispositivo
sendNotification(context,"E' arrivata la tua prima notifica attraverso GCM!");
}
}
}
private void sendNotification(Context ctx,String msg)
{
mNotificationManager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
// scelta suoneria per notifica
Uri sound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(ctx)
.setSmallIcon(R.drawable.gcm_img)
.setContentTitle("Push Notifications: primo esperimento")
.setContentText(msg)
.setSound(sound);
// effettua la notifica
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
}
}
Anche in questo caso troviamo la classe GoogleCloudMessaging
, che viene impiegata per riconoscere se il tipo di messaggio contenuto nell'Intent
è
compatibile con una notifica push.
La reazione alla ricezione della notifica push è implementata nel metodo sendNotification, che produce una comunicazione nel Notification Drawer
come quella visibile in figura:
Con questa lezione si conclude il percorso che ha portato a conoscere come le notifiche push funzionino su Android. Sebbene la loro implementazione può apparire inizialmente complicata, spesso questo meccanismo fornisce risposta a molte domande che i programmatori si pongono su tempistiche e modalità di sincronizzazione di app e applicazioni remote. Inoltre è bene non trascurare il grande contributo di efficienza che queste notifiche portano con sé: riduzione degli accessi in Rete, ottimizzazione del traffico, minore sovraccarico di richieste sui server remoti che forniscono i dati.