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());
    }
}
Explanation:
  • 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);
});
Explanation: The token can change (app reinstall, data clear, etc.), so production apps typically also override onNewToken() in FirebaseMessagingService to re-send an updated token whenever it changes.

Notification Messages vs Data Messages

FCM messages come in two shapes, and the difference matters for how reliably your code runs:

TypeWhen app is foregroundedWhen app is backgrounded/killed
Notification messageDelivered to onMessageReceived()Shown automatically by the system tray — your code never runs
Data messageDelivered to onMessageReceived()Also delivered to onMessageReceived() — your code decides what to do
Why this matters: a notification message sent while RecipeBox is backgrounded bypasses your Java code entirely — the system just displays it. If you need guaranteed custom logic (e.g., updating a local recipe cache before showing a notification), send a data message instead, so onMessageReceived() always runs and you build the notification yourself.

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.

With push notifications in place, the next lesson covers Firebase Storage — how RecipeBox uploads and stores the actual recipe photo files that go alongside the recipe data already living in Firestore.