Cloud Messaging (FCM)
Firebase Cloud Messaging (FCM) is the standard way to deliver push notifications to an Android app. It gives RecipeBox a way to reach a user even when the app isn't open — for example, letting everyone in a shared family cookbook know the moment someone adds a new recipe.
Your backend
Triggers a push, targets a device token
FCM servers
Route the message to the right device
RecipeBox app
Receives it, shows a notification
Receiving Messages: FirebaseMessagingService
To handle incoming pushes while the app is running in the foreground, extend FirebaseMessagingService and override onMessageReceived(). This connects directly back to Notifications & Channels — FCM delivers the message, but building and showing the actual notification uses the same NotificationCompat.Builder APIs from that lesson.
// RecipeMessagingService.java
public class RecipeMessagingService extends FirebaseMessagingService {
@Override
public void onMessageReceived(@NonNull RemoteMessage message) {
super.onMessageReceived(message);
String title = message.getNotification() != null
? message.getNotification().getTitle() : "RecipeBox";
String body = message.getNotification() != null
? message.getNotification().getBody() : "";
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "recipe_updates")
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(body)
.setAutoCancel(true);
NotificationManagerCompat.from(this).notify(1001, builder.build());
}
}
- onMessageReceived() only fires reliably like this while RecipeBox is in the foreground; see the notification-type distinction below for background behavior.
- The notification itself is built with the exact same NotificationCompat.Builder pattern already covered in Notifications & Channels — FCM is just what triggers it here.
The Device Token
FCM identifies each installed app instance with a unique device token — a long string your backend uses as the "address" to send that specific device a push. A real app sends this token to its own server and associates it with the currently signed-in user (from Firebase Auth), so the server knows which token belongs to which person.
FirebaseMessaging.getInstance().getToken().addOnCompleteListener(task -> {
if (!task.isSuccessful()) {
Log.e("RecipeBox", "Fetching token failed", task.getException());
return;
}
String token = task.getResult();
Log.d("RecipeBox", "Device token: " + token);
// sendTokenToBackend(currentUserUid, token);
});
Notification Messages vs Data Messages
FCM messages come in two shapes, and the difference matters for how reliably your code runs:
| Type | When app is foregrounded | When app is backgrounded/killed |
|---|---|---|
| Notification message | Delivered to onMessageReceived() | Shown automatically by the system tray — your code never runs |
| Data message | Delivered to onMessageReceived() | Also delivered to onMessageReceived() — your code decides what to do |
RecipeBox Use Case: Shared Cookbook Alerts
A natural fit for FCM in RecipeBox: when one family member adds a recipe to a shared cookbook, a backend function sends a data message titled something like "A new recipe was added to your shared family cookbook!" to every other member's device token, and onMessageReceived() shows it as a local notification, regardless of whether the app was open at the time.
Testing with the Firebase Console
Before wiring up a full backend, FCM setup can be verified end to end using the Firebase Console's built-in test tool: open Cloud Messaging → New notification, write a test title and body, target the app by package name (or paste a single device token for one specific device), and send. If onMessageReceived() fires and a notification appears, the whole pipeline — from Firebase project setup through to the on-device notification — is confirmed working.