
Guida completa a Firebase Cloud Messaging (FCM) per Android (Java)
Firebase Cloud Messaging (FCM) è il servizio di messaggistica cloud di Google che permette di inviare notifiche push e messaggi dati a dispositivi Android, iOS e al web. In questo tutorial pratico, quindi, vedremo passo passo come integrare FCM in un’app Android scritta in Java, dalla configurazione iniziale fino all’invio di notifiche da console e da server.
1. Perché usare Firebase Cloud Messaging Android
FCM è una soluzione scalabile, affidabile e gratuita per:
- In primo luogo, inviare notifiche push personalizzate agli utenti.
- Inoltre, gestire campagne di engagement (promozioni, reminder, aggiornamenti).
- In particolare, inviare messaggi silenziosi (solo dati) per sincronizzare contenuti in background.
- Allo stesso modo, raggiungere singoli dispositivi, gruppi di dispositivi o interi segmenti tramite topic.
L’integrazione con il resto dell’ecosistema Firebase (Analytics, Remote Config, A/B Testing), pertanto, lo rende uno strumento potente per gestire le comunicazioni della tua app.
2. Configurazione Firebase Cloud Messaging Android
2.1 Creare il progetto Firebase per Android
1. In primo luogo, vai su Firebase Console e crea un nuovo progetto.
2. In seguito, aggiungi un’app Android al progetto inserendo il package name esatto della tua app (es: com.example.myapp).
3. Infine, scarica il file google-services.json e posizionalo nella cartella app/ del tuo progetto Android (a livello del file app/build.gradle).
2.2 Aggiornare i file Gradle per FCM Android
Nel file build.gradle a livello di progetto (Project), in primo luogo, configura le dipendenze richieste da Firebase.
buildscript {
dependencies {
// Plugin Google Services
classpath 'com.google.gms:google-services:4.4.2'
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
Nel file app/build.gradle del modulo app, invece, aggiungi le dipendenze specifiche per FCM e per i servizi di Google Play.
plugins {
id 'com.android.application'
id 'com.google.gms.google-services' // plugin Google Services
}
android {
namespace 'com.example.myapp'
compileSdk 34
defaultConfig {
applicationId 'com.example.myapp'
minSdk 21
targetSdk 34
versionCode 1
versionName '1.0'
}
}
dependencies {
// Firebase BOM per gestire in modo allineato le versioni
implementation platform('com.google.firebase:firebase-bom:33.2.0')
// Messaging
implementation 'com.google.firebase:firebase-messaging'
}
Infine, sincronizza il progetto con Gradle, in modo che tutte le modifiche vengano applicate correttamente.
3. Implementazione di FirebaseMessagingService in Android
Per ricevere i messaggi FCM quando l’app è in foreground (aperta) o in background (ma ancora in memoria), bisogna creare una classe che estende FirebaseMessagingService; in questo modo, infatti, potrai intercettare e gestire i messaggi in arrivo.
3.1 Creare la classe MyFirebaseMessagingService per FCM Android
package com.example.myapp;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.util.Log;
import androidx.core.app.NotificationCompat;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "MyFirebaseMsgService";
private static final String CHANNEL_ID = "fcm_default_channel";
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
// Log di debug
Log.d(TAG, "From: " + remoteMessage.getFrom());
// Controlla se il messaggio contiene un payload dati
if (remoteMessage.getData().size() > 0) {
Log.d(TAG, "Message data payload: " + remoteMessage.getData());
}
// Controlla se il messaggio contiene un payload notifica
if (remoteMessage.getNotification() != null) {
Log.d(TAG, "Message Notification Body: "
+ remoteMessage.getNotification().getBody());
}
// In questo esempio mostriamo sempre una notifica locale personalizzata
String title = remoteMessage.getNotification() != null
? remoteMessage.getNotification().getTitle()
: "Nuovo messaggio";
String body = remoteMessage.getNotification() != null
? remoteMessage.getNotification().getBody()
: remoteMessage.getData().get("body");
sendNotification(title, body);
}
// Metodo per mostrare una notifica locale (vedi sezione 5 per dettagli)
private void sendNotification(String title, String messageBody) {
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(
this,
0,
intent,
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
? PendingIntent.FLAG_IMMUTABLE
: PendingIntent.FLAG_ONE_SHOT
);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Canale di notifica per Android 8.0+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence channelName = "Notifiche generali";
String channelDescription = "Canale predefinito per le notifiche FCM";
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
channelName,
importance
);
channel.setDescription(channelDescription);
notificationManager.createNotificationChannel(channel);
}
notificationManager.notify(0, notificationBuilder.build());
}
}
3.2 Dichiarare il servizio nel Manifest
Nel file AndroidManifest.xml, in primo luogo, aggiungi il servizio e i permessi internet, se non presenti:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApp">
<service
android:name=".MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
4. Gestione del token FCM Android con onNewToken
Ogni dispositivo ha un token FCM univoco che identifica l’istanza dell’app. Questo token può cambiare, quindi è importante gestire l’evento di rinnovo tramite onNewToken e, di conseguenza, aggiornare il token salvato sul tuo server.
@Override
public void onNewToken(String token) {
super.onNewToken(token);
Log.d(TAG, "Refreshed token: " + token);
// Invia il token al tuo server per associarlo all'utente
sendRegistrationToServer(token);
}
private void sendRegistrationToServer(String token) {
// TODO: invia il token al tuo backend tramite API REST
// Ad esempio con Retrofit o HttpUrlConnection
}
Puoi anche recuperare manualmente il token (ad esempio in MainActivity); in questo modo, per esempio, puoi mostrarlo nei log o inviarlo immediatamente al tuo backend.
FirebaseMessaging.getInstance().getToken()
.addOnCompleteListener(task -> {
if (!task.isSuccessful()) {
Log.w(TAG, "Fetching FCM registration token failed", task.getException());
return;
}
String token = task.getResult();
Log.d(TAG, "Current token: " + token);
});
5. Mostrare una notifica locale FCM Android
Nell’esempio della sezione 3 abbiamo già implementato il metodo sendNotification. Di seguito, quindi, lo riportiamo separatamente con commenti dettagliati, in modo da chiarire ogni singolo passaggio.
private void sendNotification(String title, String messageBody) {
// Intent per aprire l'attività principale quando l'utente tocca la notifica
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// PendingIntent richiesto per le notifiche
PendingIntent pendingIntent = PendingIntent.getActivity(
this,
0,
intent,
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
? PendingIntent.FLAG_IMMUTABLE
: PendingIntent.FLAG_ONE_SHOT
);
// Suono di default per la notifica
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
// Creazione della notifica
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification) // Icona piccola obbligatoria
.setContentTitle(title)
.setContentText(messageBody)
.setAutoCancel(true) // Chiudi la notifica quando viene toccata
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Per Android 8.0+ è obbligatorio il NotificationChannel
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence channelName = "Notifiche generali";
String channelDescription = "Canale predefinito per le notifiche FCM";
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
channelName,
importance
);
channel.setDescription(channelDescription);
notificationManager.createNotificationChannel(channel);
}
// Mostra la notifica (ID 0, puoi usare ID diversi per notifiche multiple)
notificationManager.notify(0, notificationBuilder.build());
}
6. Notifiche FCM Android: Differenza tra Foreground e Background
Il comportamento dei messaggi FCM, in altre parole, dipende dallo stato dell’app e dal tipo di payload:
- App in foreground:
- In questo caso, i messaggi con payload dati (solo
data) vengono consegnati aonMessageReceived. - Inoltre, i messaggi con payload notifica + dati arrivano comunque a
onMessageReceived(su Android) e puoi gestirli per mostrare una notifica personalizzata.
- In questo caso, i messaggi con payload dati (solo
- App in background o chiusa:
- In questo scenario, i messaggi con payload notifica sono gestiti automaticamente da FCM: Android mostra la notifica usando il titolo e il corpo definiti nel messaggio, mentre
onMessageReceivedpotrebbe non essere chiamato in tutti i casi. - Invece, i messaggi con solo payload dati vengono recapitati a
onMessageReceived(dipende dalle condizioni di sistema e di rete) e non viene mostrata automaticamente alcuna notifica: di conseguenza, devi mostrarla tu.
- In questo scenario, i messaggi con payload notifica sono gestiti automaticamente da FCM: Android mostra la notifica usando il titolo e il corpo definiti nel messaggio, mentre
Per un controllo completo del comportamento, pertanto, è consigliabile usare principalmente messaggi dati e costruire manualmente le notifiche nell’app.
7. Invio di messaggi dalla Firebase Console
La Firebase Console, in altre parole, è il modo più semplice per iniziare a testare FCM:
- In primo luogo, vai su Firebase Console > Cloud Messaging.
- In seguito, clicca su Invia il tuo primo messaggio (o “Nuovo messaggio”).
- Quindi, inserisci titolo e testo della notifica.
- In Target, invece, scegli l’app Android di destinazione.
- In Schedulazione puoi inviare subito o, in alternativa, pianificare.
- In Opzioni avanzate, inoltre, puoi aggiungere una chiave dati personalizzata (ad esempio
screen=promo). - Infine, clicca su Invia.
I messaggi inviati da console usano di default un payload notifica. Se vuoi testare payload dati puri, invece, è meglio usare le API server o strumenti come Postman.
8. Invio Messaggi FCM Android da Server (HTTP API)
Per inviare notifiche da un server puoi usare l’HTTP v1 API di FCM; in questo modo, per esempio, puoi integrare l’invio di messaggi in un backend esistente o in un sistema di automazione.
8.1 Ottenere le credenziali server per FCM Android
1. In Firebase Console, in primo luogo, vai su Impostazioni progetto > Account di servizio.
2. In seguito, clicca su Genera nuova chiave privata per ottenere un JSON di credenziali.
3. Infine, usa questa chiave nel tuo backend per generare un token OAuth 2.0 o per configurare l’SDK Admin di Firebase.
8.2 Esempio di richiesta HTTP v1 (JSON)
URL di destinazione (sostituisci PROJECT_ID con l’ID del tuo progetto); in questo modo, la richiesta raggiungerà correttamente l’istanza FCM associata:
POST https://fcm.googleapis.com/v1/projects/PROJECT_ID/messages:send
Authorization: Bearer <ACCESS_TOKEN>
Content-Type: application/json
Body di esempio per inviare a un singolo token; in particolare, questo payload mostra come combinare campi notifica e dati:
{
"message": {
"token": "DEVICE_FCM_TOKEN",
"notification": {
"title": "Offerta speciale",
"body": "Sconto del 20% solo per oggi!"
},
"data": {
"screen": "offers",
"discount": "20"
}
}
}
Nel tuo server dovrai generare <ACCESS_TOKEN> usando le credenziali del service account; a tal fine, puoi seguire la documentazione ufficiale di FCM Admin SDK per il linguaggio che preferisci.
9. Invio a topic (subscribe / unsubscribe)
I topic permettono di inviare messaggi a gruppi logici di dispositivi (ad esempio tutti gli utenti interessati alle notizie sportive). I client, in questo modo, si iscrivono ai topic direttamente dall’app.
Articoli correlati
- Notifiche Android: Guida Completa con NotificationCompat ed Esempi Java
- BigPictureStyle Android: Notifiche con Immagine Grande ed Esempi Java
- BigTextStyle Android: Notifiche con Testo Esteso ed Esempi Java
- InboxStyle Android: Notifiche a Lista con Esempi Java
- BroadcastReceiver Android: Guida Completa con Esempi Java
9.1 Iscrizione e disiscrizione ai topic in Java
import com.google.firebase.messaging.FirebaseMessaging;
// Iscrizione a un topic
FirebaseMessaging.getInstance().subscribeToTopic("news")
.addOnCompleteListener(task -> {
if (task.isSuccessful()) {
Log.d(TAG, "Iscritto al topic news");
} else {
Log.w(TAG, "Errore iscrizione topic news", task.getException());
}
});
// Disiscrizione da un topic
FirebaseMessaging.getInstance().unsubscribeFromTopic("news")
.addOnCompleteListener(task -> {
if (task.isSuccessful()) {
Log.d(TAG, "Disiscritto dal topic news");
} else {
Log.w(TAG, "Errore disiscrizione topic news", task.getException());
}
});
9.2 Invio di un messaggio a un topic da server
{
"message": {
"topic": "news",
"notification": {
"title": "Ultime notizie",
"body": "Leggi ora gli aggiornamenti in tempo reale."
},
"data": {
"category": "sport"
}
}
}
10. Payload dati vs payload notifica in Firebase Cloud Messaging Android
FCM supporta due tipi principali di payload, vale a dire:
- Payload notifica (
notification):- Contiene campi come
title,body,icon; in questo modo, il sistema può costruire direttamente la notifica. - È gestito automaticamente dal sistema quando l’app è in background; di conseguenza, non devi creare manualmente la notifica.
- È più semplice da usare ma, tuttavia, è meno flessibile.
- Contiene campi come
- Payload dati (
data):- Contiene coppie chiave/valore definite da te (tutte stringhe) e, in particolare, consente di modellare il contenuto in base alle esigenze della tua app.
- È sempre consegnato a
onMessageReceived(nei limiti delle condizioni di sistema) e, di conseguenza, ti permette di eseguire logica personalizzata. - Ti permette di decidere come e quando mostrare notifiche e cosa fare al tap; in questo modo, ottieni il massimo controllo sull’esperienza utente.
10.1 Esempio solo notifiche
{
"message": {
"token": "DEVICE_FCM_TOKEN",
"notification": {
"title": "Benvenuto",
"body": "Grazie per aver installato la nostra app!"
}
}
}
10.2 Esempio solo dati
{
"message": {
"token": "DEVICE_FCM_TOKEN",
"data": {
"type": "sync",
"entity": "messages",
"forceRefresh": "true"
}
}
}
Nel caso “solo dati”, sarai tu, in onMessageReceived, a decidere se mostrare una notifica e con quale contenuto; in conclusione, l’intero comportamento sarà definito dalla logica della tua applicazione.
11. Esempio completo: MainActivity in Java
Per concludere, ecco un esempio di MainActivity minimale in Java che inizializza Firebase e mostra il token corrente nei log, in modo che tu possa verificare facilmente il corretto funzionamento.
package com.example.myapp;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.google.firebase.FirebaseApp;
import com.google.firebase.messaging.FirebaseMessaging;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Inizializza Firebase (in genere è automatico, ma puoi forzarlo se necessario)
FirebaseApp.initializeApp(this);
// Recupera il token FCM corrente
FirebaseMessaging.getInstance().getToken()
.addOnCompleteListener(task -> {
if (!task.isSuccessful()) {
Log.w(TAG, "Fetching FCM registration token failed", task.getException());
return;
}
String token = task.getResult();
Log.d(TAG, "FCM token: " + token);
});
// Esempio: iscrizione a un topic
FirebaseMessaging.getInstance().subscribeToTopic("news")
.addOnCompleteListener(task -> {
if (task.isSuccessful()) {
Log.d(TAG, "Iscritto al topic news");
} else {
Log.w(TAG, "Errore iscrizione topic news", task.getException());
}
});
}
}
Con questa configurazione, in conclusione, hai una base completa per lavorare con Firebase Cloud Messaging in Android usando Java: puoi ricevere messaggi, gestire token, mostrare notifiche personalizzate, usare topic e integrare un backend per l’invio di campagne push avanzate.
Rispondi