Topic Messaging on Android

Based on the publish/subscribe model, FCM topic messaging allows you to send a message to multiple devices that have opted in to a particular topic. You compose topic messages as needed, and FCM handles routing and delivering the message reliably to the right devices.

For example, users of a local weather forecasting app could opt in to a "severe weather alerts" topic and receive notifications of storms threatening specified areas. Users of a sports app could subscribe to automatic updates in live game scores for their favorite teams.

Some things to keep in mind about topics:

  • Topic messaging supports unlimited topics and subscriptions for each app.
  • Topic messaging is best suited for content such as news, weather, or other publicly available information.
  • Topic messages are optimized for throughput rather than latency. For fast, secure delivery to single devices or small groups of devices, target messages to registration tokens, not topics.
  • If you need to send messages to multiple devices per user, consider device group messaging for those use cases.

Subscribe the client app to a topic

Client apps can subscribe to any existing topic, or they can create a new topic. When a client app subscribes to a new topic name (one that does not already exist for your Firebase project), a new topic of that name is created in FCM and any client can subsequently subscribe to it.

To subscribe to a topic, the client app calls Firebase Cloud Messaging subscribeToTopic() with the FCM topic name:

FirebaseMessaging.getInstance().subscribeToTopic("news");

To unsubscribe, the client app calls Firebase Cloud Messaging unsubscribeFromTopic() with the topic name.

Manage topic subscriptions on the server

You can take advantage of Instance ID APIs to perform basic topic management tasks from the server side. Given the registration token(s) of client app instances, you can do the following:

Receive and handle topic messages

FCM delivers topic messages in the same way as other downstream messages.

To receive messages, use a service that extends FirebaseMessagingService. Your service should override the onMessageReceived and onDeletedMessages callbacks. It should handle any message within 10 seconds of receipt. After that, Android does not guarantee execution, and could terminate your process at any time. If your app needs more time to process a message, use the Firebase Job Dispatcher.

onMessageReceived is provided for most message types, with the following exceptions:

  • Notifications delivered when your app is in the background. In this case, the notification is delivered to the device’s system tray. A user tap on a notification opens the app launcher by default.

  • Messages with both notification and data payload, both background and foreground. In this case, the notification is delivered to the device’s system tray, and the data payload is delivered in the extras of the intent of your launcher Activity.

In summary:

App state Notification Data Both
Foreground onMessageReceived onMessageReceived onMessageReceived
Background System tray onMessageReceived Notification: system tray
Data: in extras of the intent.
For more information about message types, see Notifications and data messages.

Edit the app manifest

To use FirebaseMessagingService, you need to add the following in your app manifest:

<service
    android:name=".MyFirebaseMessagingService">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT"/>
    </intent-filter>
</service>

Also, you're recommended to set default values to customize the appearance of notifications. You can specify a custom default icon and a custom default color that are applied whenever equivalent values are not set in the notification payload.

Add these lines inside the application tag to set the custom default icon and custom color:

<!-- Set custom default icon. This is used when no icon is set for incoming notification messages.
     See README(https://goo.gl/l4GJaQ) for more. -->
<meta-data
    android:name="com.google.firebase.messaging.default_notification_icon"
    android:resource="@drawable/ic_stat_ic_notification" />
<!-- Set color used with incoming notification messages. This is used when no color is set for the incoming
     notification message. See README(https://goo.gl/6BKBk7) for more. -->
<meta-data
    android:name="com.google.firebase.messaging.default_notification_color"
    android:resource="@color/colorAccent" />

Android displays the custom default icon for

  • All notification messages sent from the Notifications console.
  • Any notification message that does not explicitly set the icon in the notification payload.

Android uses the custom default color for

  • All notification messages sent from the Notifications console.
  • Any notification message that does not explicitly set the color in the notification payload.

If no custom default icon is set and no icon is set in the notification payload, Android displays the application icon rendered in white.

Override onMessageReceived

By overriding the method FirebaseMessagingService.onMessageReceived, you can perform actions based on the received RemoteMessage object and get the message data:

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
    // ...

    // TODO(developer): Handle FCM messages here.
    // Not getting messages here? See why this may be: https://goo.gl/39bRNJ
    Log.d(TAG, "From: " + remoteMessage.getFrom());

    // Check if message contains a data payload.
    if (remoteMessage.getData().size() > 0) {
        Log.d(TAG, "Message data payload: " + remoteMessage.getData());

        if (/* Check if data needs to be processed by long running job */ true) {
            // For long-running tasks (10 seconds or more) use Firebase Job Dispatcher.
            scheduleJob();
        } else {
            // Handle message within 10 seconds
            handleNow();
        }

    }

    // Check if message contains a notification payload.
    if (remoteMessage.getNotification() != null) {
        Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
    }

    // Also if you intend on generating your own notifications as a result of a received FCM
    // message, here is where that should be initiated. See sendNotification method below.
}

Override onDeletedMessages

In some situations, FCM may not deliver a message. This occurs when there are too many messages (>100) pending for your app on a particular device at the time it connects or if the device hasn't connected to FCM in more than one month. In these cases, you may receive a callback to FirebaseMessagingService.onDeletedMessages(). When the app instance receives this callback, it should perform a full sync with your app server. If you haven't sent a message to the app on that device within the last 4 weeks, FCM won't call onDeletedMessages().

Handle notification messages in a backgrounded app

When your app is in the background, Android directs notification messages to the system tray. A user tap on the notification opens the app launcher by default.

This includes messages that contain both notification and data payload (and all messages sent from the Notifications console). In these cases, the notification is delivered to the device's system tray, and the data payload is delivered in the extras of the intent of your launcher Activity.

Build send requests

From the server side, sending messages to a Firebase Cloud Messaging topic is very similar to sending messages to an individual device or to a user group. The app server sets the to key with a value like /topics/yourTopic. Developers can choose any topic name that matches the regular expression: "/topics/[a-zA-Z0-9-_.~%]+".

To send to combinations of multiple topics, the app server sets the condition key to a boolean condition that specifies the target topics. For example, to send messages to devices that subscribed to TopicA and either TopicB or TopicC:

'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)

FCM first evaluates any conditions in parentheses, and then evaluates the expression from left to right. In the above expression, a user subscribed to any single topic does not receive the message. Likewise, a user who does not subscribe to TopicA does not receive the message. These combinations do receive it:

  • TopicA and TopicB
  • TopicA and TopicC

Conditions for topics support two operators per expression, and parentheses are supported.

For more detail about app server keys, see the reference information for your chosen connection server protocol, HTTP or XMPP. Examples in this page show how to send messages to topics in both HTTP and XMPP.

HTTP POST request

Send to a single topic:

https://fcm.googleapis.com/fcm/send
Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA
{
  "to": "/topics/foo-bar",
  "data": {
    "message": "This is a Firebase Cloud Messaging Topic Message!",
   }
}

Send to devices subscribed to topics "dogs" or "cats":

https://fcm.googleapis.com/fcm/send
Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA
{
  "condition": "'dogs' in topics || 'cats' in topics",
  "data": {
    "message": "This is a Firebase Cloud Messaging Topic Message!",
   }
}

HTTP response

//Success example:
{
  "message_id": "1023456"
}

//failure example:
{
  "error": "TopicsMessageRateExceeded"
}

XMPP message

Send to a single topic:

<message id="">
  <gcm xmlns="google:mobile:data">
{
  "to": "/topics/foo-bar",
  "data": {
    "message": "This is a Firebase Cloud Messaging Topic Message!",
   }
}

  </gcm>
</message>

Send to devices subscribed to topics "dogs" or "cats":

<message id="">
  <gcm xmlns="google:mobile:data">
{
  "condition": "'dogs' in topics || 'cats' in topics",
  "data": {
    "message": "This is a Firebase Cloud Messaging Topic Message!",
   }
}

XMPP response

//Success example:
{
  "message_id": "1023456"
}

//failure example:
{
  "error": "TopicsMessageRateExceeded"
}

Expect up to 30 seconds of delay before the FCM Connection Server returns a success or failure response to the topic send requests. Make sure to set the app server's timeout value in the request accordingly.

For the full list of message options, see the reference information for your chosen connection server protocol, HTTP or XMPP.

Next steps

Send feedback about...

Need help? Visit our support page.