Handling Firebase Cloud Messaging Notifications in Background State: Implementation and Best Practices

Nov 09, 2025 · Programming · 29 views · 7.8

Keywords: Firebase Cloud Messaging | Android Push Notifications | Background Message Handling | Data Messages | onMessageReceived | Custom Notifications

Abstract: This technical paper provides an in-depth analysis of Firebase Cloud Messaging message handling mechanisms on Android platforms, focusing on the fundamental reasons why onMessageReceived method is not invoked when applications run in background. By comparing display messages and data messages, it elaborates on how to ensure proper push notification processing in any application state through pure data messages. The paper offers comprehensive implementation solutions including server-side API specifications, client-side code implementation, and custom notification building methods to help developers completely resolve background message handling issues.

Understanding Firebase Cloud Messaging Message Types

Within the Firebase Cloud Messaging ecosystem, messages are distinctly categorized into two core types, each possessing unique behavioral characteristics and processing mechanisms. Comprehending the fundamental differences between these message types is crucial for resolving background message handling challenges.

Display Messages are primarily designed for presenting visible notification content to users. These messages contain predefined visual elements such as titles and body text, with the system automatically handling notification presentation. However, a significant limitation of display messages is that the onMessageReceived callback method is only triggered when the application runs in the foreground. When the application transitions to background or is completely terminated, the system takes over the notification display process, preventing execution of developer-defined custom logic.

In contrast, Data Messages offer a more flexible processing mechanism. Data messages do not contain predefined display content but instead deliver custom data in key-value pair format. This design enables the onMessageReceived method to be invoked in any application state—whether running in foreground, suspended in background, or completely terminated and restarted. Data messages return complete control to developers, allowing customization of notification generation and display logic according to business requirements.

Server-Side Message Delivery Implementation

To ensure proper push message processing in background states, applications must receive pure data messages sent from the server side. Firebase Console's Notifications composer currently only supports sending display messages, necessitating developers to implement server-side logic independently.

Data message delivery requires invoking the FCM service interface through HTTP POST requests, which must contain correct authentication information and message structure. Below is the complete implementation specification:

POST https://fcm.googleapis.com/fcm/send

Headers:
Content-Type: application/json
Authorization: key=<your-server-key>

Body (sending to specific topic):
{
    "to": "/topics/my_topic",
    "data": {
        "title": "Notification Title",
        "message": "Notification body content",
        "image_url": "https://example.com/image.jpg",
        "action": "custom_action"
    }
}

Body (sending to specific devices):
{
    "data": {
        "title": "Notification Title",
        "message": "Notification body content",
        "image_url": "https://example.com/image.jpg",
        "action": "custom_action"
    },
    "registration_ids": ["{device-token}","{device2-token}","{device3-token}"]
}

Critical considerations: The message body must avoid including the notification field, as its presence converts the message into a display message, consequently preventing onMessageReceived invocation in background states. The server key can be located in Firebase Console project settings, specifically at: Project Settings → Cloud Messaging → Server Key.

Client-Side Message Processing Implementation

On the Android client side, developers need to extend the FirebaseMessagingService class and override the onMessageReceived method to handle received data messages. Below is a complete implementation example:

public class CustomFirebaseMessagingService extends FirebaseMessagingService {
    private static final String TAG = "FCMService";

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        // Retrieve data payload from message
        Map<String, String> data = remoteMessage.getData();
        
        // Parse custom data fields
        String title = data.get("title");
        String message = data.get("message");
        String imageUrl = data.get("image_url");
        String action = data.get("action");

        // Log for debugging purposes
        Log.i(TAG, "Message received - Title: " + title);
        Log.i(TAG, "Message received - Message: " + message);
        Log.i(TAG, "Message received - Image URL: " + imageUrl);
        Log.i(TAG, "Message received - Action: " + action);

        // Determine notification type based on image URL presence
        if (imageUrl == null || imageUrl.isEmpty()) {
            createBasicNotification(title, message, action);
        } else {
            createBigPictureNotification(title, message, imageUrl, action);
        }
    }

    private void createBasicNotification(String title, String message, String action) {
        // Create notification channel (required for Android 8.0+)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                "default_channel", 
                "Default Notifications", 
                NotificationManager.IMPORTANCE_HIGH
            );
            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(channel);
        }

        // Build notification
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "default_channel")
            .setSmallIcon(R.drawable.ic_notification)
            .setContentTitle(title)
            .setContentText(message)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setAutoCancel(true);

        // Create click intent
        Intent intent = new Intent(this, MainActivity.class);
        intent.putExtra("action", action);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        
        PendingIntent pendingIntent = PendingIntent.getActivity(
            this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
        );
        builder.setContentIntent(pendingIntent);

        // Display notification
        NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
        notificationManager.notify(generateNotificationId(), builder.build());
    }

    private void createBigPictureNotification(String title, String message, String imageUrl, String action) {
        // Implement big picture notification logic
        // Including image downloading, big picture style configuration, etc.
    }

    private int generateNotificationId() {
        return (int) System.currentTimeMillis();
    }
}

AndroidManifest Configuration

Proper service declaration is essential for ensuring FCM functionality. The message handling service must be declared in AndroidManifest.xml:

<service
    android:name=".fcm.CustomFirebaseMessagingService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

The android:exported="false" attribute ensures the service can only be invoked by the system, enhancing application security.

Advanced Features and Best Practices

In practical development, several advanced features and best practices should be considered to optimize user experience:

Notification Channel Management: Starting from Android 8.0, notification channels must be created. Proper channel categorization helps users better manage notification preferences.

Big Picture Notification Handling: When messages contain image URLs, images should be downloaded asynchronously before displaying notifications. Using image loading libraries like Glide or Picasso is recommended for handling image downloading and caching.

Message Deduplication: To avoid duplicate notifications, generate unique notification IDs for each message or implement deduplication mechanisms based on business logic.

Background Processing Optimization: Avoid performing time-consuming operations within onMessageReceived. Use WorkManager or JobScheduler when necessary to schedule background tasks.

Testing and Debugging

A comprehensive testing process is crucial for ensuring functionality:

First, verify server-side message delivery functionality to ensure correct FCM API invocation and successful responses. Then test message reception in different application states: verify onMessageReceived invocation when application is in foreground; verify custom notification display when application is in background; verify proper application launch and parameter passing when notifications are clicked after application termination.

Use Android Studio's Logcat to monitor log output, ensuring correct message parsing and processing logic execution. Simultaneously test edge cases such as network exceptions and image loading failures to ensure application robustness.

Conclusion

By utilizing pure data messages instead of display messages, developers gain complete control over push notification processing workflows, ensuring execution of custom logic in any application state. Although this approach requires more implementation effort, it provides significant flexibility and control capabilities to meet complex business requirements.

Key success factors include: correct server-side message structure, complete client-side service implementation, reasonable notification building logic, and comprehensive testing validation. By following the implementation solution described in this paper, developers can completely resolve Firebase Cloud Messaging message handling issues in background states.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.