Stay organized with collectionsSave and categorize content based on your preferences.
To target a message to multiple devices, useTopic messaging. This
feature allows you to
send a message to multiple devices that have opted in to a particular topic.
This tutorial focuses on sending topic messages from your app server using theAdmin SDKorREST APIforFCM,
and receiving and handling
them in an android app. We'll cover message handling for both backgrounded and
foregrounded apps. All the steps to achieve this are covered, from setup to
verification.
If you don't already have an Android project and just want to try out a Firebase
product, you can download one of ourquickstart samples.
Create a Firebase project
Before you can add Firebase to your Android app, you need to create a Firebase
project to connect to your Android app. VisitUnderstand Firebase Projectsto learn more about
Firebase projects.
To use Firebase in your Android app, you need to register your app with your
Firebase project. Registering your app is often called "adding" your app to your
project.
In the center of the project overview page, click theAndroidicon
(plat_android)
orAdd appto launch the setup workflow.
Enter your app's package name in theAndroid package namefield.
What's a package name, and where do you find it?
Apackage
nameuniquely identifies your app on the device and in the Google Play Store.
Apackage nameis often referred to as anapplication ID.
Find your app's package name in your module (app-level) Gradle file,
usuallyapp/build.gradle(example package name:com.yourcompany.yourproject).
Be aware that the package name value is case-sensitive, and it cannot be
changed for this Firebase Android app after it's registered with your
Firebase project.
(Optional)Enter other app information:App nicknameandDebug signing certificate SHA-1.
How are theApp nicknameand theDebug signing certificate SHA-1used within Firebase?
App nickname: An internal, convenience identifier that is only
visible to you in theFirebaseconsole
Download and then add your app's Firebase config file
(google-services.json) to your codebase:
ClickDownload google-services.jsonto obtain your app's Firebase
config file.
Move your config file into themodule (app-level)root directory of
your app.
What do you need to know about this config file?
The Firebase config file contains unique, but non-secret identifiers for
your project and app. To learn more about this config file, visitUnderstand Firebase
Projects.
Make sure the config file name is not appended with additional characters,
like(2).
To make the values in yourgoogle-services.jsonconfig file accessible
to Firebase SDKs, you need theGoogle services Gradle plugin(google-services).
In yourroot-level (project-level)Gradle file
(<project>/build.gradle.ktsor<project>/build.gradle), add the
Google services plugin as a dependency:
Kotlin
plugins{id("com.android.application")version"7.3.0"applyfalse// ...// Add the dependency for the Google services Gradle pluginid("com.google.gms.google-services")version"4.4.3"applyfalse}
Groovy
plugins{id'com.android.application'version'7.3.0'applyfalse// ...// Add the dependency for the Google services Gradle pluginid'com.google.gms.google-services'version'4.4.3'applyfalse}
In yourmodule (app-level)Gradle file
(usually<project>/<app-module>/build.gradle.ktsor<project>/<app-module>/build.gradle),
add the Google services plugin:
Kotlin
plugins{id("com.android.application")// Add the Google services Gradle pluginid("com.google.gms.google-services")// ...}
Groovy
plugins{id'com.android.application'// Add the Google services Gradle pluginid'com.google.gms.google-services'// ...}
Add Firebase SDKs to your app
In yourmodule (app-level) Gradle file(usually<project>/<app-module>/build.gradle.ktsor<project>/<app-module>/build.gradle),
add the dependency for theFirebase Cloud Messaginglibrary for Android. We recommend using theFirebase Android BoMto control library versioning.
For an optimal experience withFirebase Cloud Messaging, we recommendenablingGoogle Analyticsin your Firebase project and adding the Firebase SDK for Google Analytics to your app.
dependencies{// Import theBoMfor the Firebase platformimplementation(platform("com.google.firebase:firebase-bom:34.2.0"))// Add the dependencies for theFirebase Cloud MessagingandAnalyticslibraries// When using theBoM, you don't specify versions in Firebase library dependenciesimplementation("com.google.firebase:firebase-messaging")implementation("com.google.firebase:firebase-analytics")}
By using theFirebase Android BoM,
your app will always use compatible versions of Firebase Android libraries.
(Alternative)
Add Firebase library dependencies without using theBoM
If you choose not to use theFirebase BoM, you must specify each Firebase library version
in its dependency line.
Note that if you usemultipleFirebase libraries in your app, we strongly
recommend using theBoMto manage library versions, which ensures that all versions are
compatible.
dependencies{// Add the dependencies for theFirebase Cloud MessagingandAnalyticslibraries// When NOT using theBoM, you must specify versions in Firebase library dependenciesimplementation("com.google.firebase:firebase-messaging:25.0.0")implementation("com.google.firebase:firebase-analytics:23.0.0")}
Sync your Android project with Gradle files.
Are you getting a build failure about invoke-custom support and enabling
desugaring? Here's how to fix it.
Gradle builds that use Android Gradle plugin (AGP) v4.2 or earlier need to
enable Java 8 support. Otherwise, these Android projects get a build
failure when adding a Firebase SDK.
To fix this build failure, you can follow one of two options:
Add the listedcompileOptionsfrom the error message to yourapp-levelbuild.gradle.ktsorbuild.gradlefile.
Increase theminSdkfor your Android project to 26 or above.
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 inFCMand any client can subsequently subscribe to it.
To subscribe to a topic, the client app callsFirebase Cloud MessagingsubscribeToTopic()with theFCMtopic name. This method
returns aTask, which can be used by a completion listener to determine whether
the subscription succeeded:
To unsubscribe, the client app callsFirebase Cloud MessagingunsubscribeFromTopic()with the topic name.
Receive and handle topic messages
FCMdelivers topic messages in the same way as other downstream
messages.
To receive messages, use a service that extendsFirebaseMessagingService.
Your service should override theonMessageReceivedandonDeletedMessagescallbacks.
onMessageReceivedis provided for most message types, with the following
exceptions:
Notification messages 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, when received in the background.
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.
TheonMessageReceivedcallback is given timeouts that enable you to simply post a
notification but the timers are not designed to allow the app to access the network or to do
additional work. As such, if your app does anything more complicated, you need to do additional
work to ensure the app can complete its work.
If you expect your app could require close to 10 seconds to handle a message, you shouldschedule a WorkManager jobor follow theWakeLock guidance below. In some
cases, the time window for handling a message may be shorter than 10 seconds depending on delays
incurred ahead of callingonMessageReceived, including OS delays, app startup time,
the main thread being blocked by other operations, or previousonMessageReceivedcalls taking too long. After that timer expires, your app may be
subject toprocess killingorbackground execution limits. Keep in mind, latencies for network transactions and app startup can be significant, so when in
doubt, plan for your message processing to run long if there are any asynchronous dependencies
such as network access or intensive data loading requirements.
Edit the app manifest
To useFirebaseMessagingService, you need to add the following in your
app manifest:
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 theapplicationtag 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.SeeREADME(https://goo.gl/l4GJaQ)formore.-->
<meta-dataandroid: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 incomingnotificationmessage.SeeREADME(https://goo.gl/6BKBk7)formore.-->
<meta-dataandroid:name="com.google.firebase.messaging.default_notification_color"android:resource="@color/colorAccent"/>
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.
OverrideonMessageReceived
By overriding the methodFirebaseMessagingService.onMessageReceived,
you can perform actions based on the receivedRemoteMessageobject and get the message data:
Kotlin
overridefunonMessageReceived(remoteMessage:RemoteMessage){// TODO(developer): Handle FCM messages here.// Not getting messages here? See why this may be: https://goo.gl/39bRNJLog.d(TAG,"From:${remoteMessage.from}")// Check if message contains a data payload.if(remoteMessage.data.isNotEmpty()){Log.d(TAG,"Message data payload:${remoteMessage.data}")// Check if data needs to be processed by long running jobif(needsToBeScheduled()){// For long-running tasks (10 seconds or more) use WorkManager.scheduleJob()}else{// Handle message within 10 secondshandleNow()}}// Check if message contains a notification payload.remoteMessage.notification?.let{Log.d(TAG,"Message Notification Body:${it.body}")}// 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.}
@OverridepublicvoidonMessageReceived(RemoteMessageremoteMessage){// TODO(developer): Handle FCM messages here.// Not getting messages here? See why this may be: https://goo.gl/39bRNJLog.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 WorkManager.scheduleJob();}else{// Handle message within 10 secondshandleNow();}}// 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.}
If your app needs to keep the device awake while processing an FCM message, then it will need to
hold a WakeLock during this time or it will need to create a WorkManager job. WakeLocks work well
for short processing activities that might exceed theonMessageReceiveddefault
timeouts. For extended workflows, such as sending multiple serial RPCs to your servers, using a
WorkManager job is more appropriate than a WakeLock. In this section we focus on how to use
WakeLocks. A WakeLock prevents the device from sleeping while your app is running, which can
result in increased battery use, so use of WakeLocks should be reserved for cases where your app
should not be paused while handling the message such as:
Notifications to the user that are time sensitive.
Interactions with something off device that shouldn't be interrupted (such as network transfers
or communications with another device, like a paired watch).
First you'll need to make sure that your app requests the WakeLock permission (the FCM SDK
includes this by default, so normally nothing needs to be added).
Then your app will need to acquire a WakeLock at the start of theFirebaseMessagingService.onMessageReceived()callback and release it at the end of the callback.
App's customFirebaseMessagingService:
@Override
public void onMessageReceived(final RemoteMessage message) {
// If this is a message that is time sensitive or shouldn't be interrupted
WakeLock wakeLock = getSystemService(PowerManager.class).newWakeLock(PARTIAL_WAKE_LOCK, "myApp:messageReceived");
try {
wakeLock.acquire(TIMEOUT_MS);
// handle message
...
finally {
wakeLock.release();
}
}
OverrideonDeletedMessages
In some situations,FCMmay 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 toFCMin more than one month. In these cases,
you may receive a callback toFirebaseMessagingService.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,FCMwon't callonDeletedMessages().
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.
For insight into message delivery to your app, see
theFCMreporting dashboard, which records the
number of messages sent and opened on Apple and Android devices, along with
data for "impressions" (notifications seen by users) for Android apps.
Build send requests
After you have created a topic, either by subscribing client app instances to
the topic on the client side or via theserver API, you can send messages to the
topic. If this is your first time building send requests forFCM,
see the guide toyour server environment andFCMfor
important background and setup information.
In your sending logic on the backend, specify the desired topic name
as shown:
Node.js
// The topic name can be optionally prefixed with "/topics/".consttopic='highScores';constmessage={data:{score:'850',time:'2:45'},topic:topic};// Send a message to devices subscribed to the provided topic.getMessaging().send(message).then((response)=>{// Response is a message ID string.console.log('Successfully sent message:',response);}).catch((error)=>{console.log('Error sending message:',error);});
Java
// The topic name can be optionally prefixed with "/topics/".Stringtopic="highScores";// See documentation on defining a message payload.Messagemessage=Message.builder().putData("score","850").putData("time","2:45").setTopic(topic).build();// Send a message to the devices subscribed to the provided topic.Stringresponse=FirebaseMessaging.getInstance().send(message);// Response is a message ID string.System.out.println("Successfully sent message: "+response);
# The topic name can be optionally prefixed with "/topics/".topic='highScores'# See documentation on defining a message payload.message=messaging.Message(data={'score':'850','time':'2:45',},topic=topic,)# Send a message to the devices subscribed to the provided topic.response=messaging.send(message)# Response is a message ID string.print('Successfully sent message:',response)
// The topic name can be optionally prefixed with "/topics/".topic:="highScores"// See documentation on defining a message payload.message:=&messaging.Message{Data:map[string]string{"score":"850","time":"2:45",},Topic:topic,}// Send a message to the devices subscribed to the provided topic.response,err:=client.Send(ctx,message)iferr!=nil{log.Fatalln(err)}// Response is a message ID string.fmt.Println("Successfully sent message:",response)
// The topic name can be optionally prefixed with "/topics/".vartopic="highScores";// See documentation on defining a message payload.varmessage=newMessage(){Data=newDictionary<string,string>(){{"score","850"},{"time","2:45"},},Topic=topic,};// Send a message to the devices subscribed to the provided topic.stringresponse=awaitFirebaseMessaging.DefaultInstance.SendAsync(message);// Response is a message ID string.Console.WriteLine("Successfully sent message: "+response);
To send a message to acombinationof topics,
specify acondition, which is a boolean expression that specifies the
target topics. For example, the following condition will send messages to
devices that are subscribed toTopicAand eitherTopicBorTopicC:
"'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)"
FCMfirst 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 toTopicAdoes not receive the message. These combinations do
receive it:
TopicAandTopicB
TopicAandTopicC
You can include up to five topics in your conditional expression.
To send to a condition:
Node.js
// Define a condition which will send to devices which are subscribed// to either the Google stock or the tech industry topics.constcondition='\'stock-GOOG\' in topics || \'industry-tech\' in topics';// See documentation on defining a message payload.constmessage={notification:{title:'$FooCorp up 1.43% on the day',body:'$FooCorp gained 11.80 points to close at 835.67, up 1.43% on the day.'},condition:condition};// Send a message to devices subscribed to the combination of topics// specified by the provided condition.getMessaging().send(message).then((response)=>{// Response is a message ID string.console.log('Successfully sent message:',response);}).catch((error)=>{console.log('Error sending message:',error);});
Java
// Define a condition which will send to devices which are subscribed// to either the Google stock or the tech industry topics.Stringcondition="'stock-GOOG' in topics || 'industry-tech' in topics";// See documentation on defining a message payload.Messagemessage=Message.builder().setNotification(Notification.builder().setTitle("$GOOG up 1.43% on the day").setBody("$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.").build()).setCondition(condition).build();// Send a message to devices subscribed to the combination of topics// specified by the provided condition.Stringresponse=FirebaseMessaging.getInstance().send(message);// Response is a message ID string.System.out.println("Successfully sent message: "+response);
# Define a condition which will send to devices which are subscribed# to either the Google stock or the tech industry topics.condition="'stock-GOOG' in topics || 'industry-tech' in topics"# See documentation on defining a message payload.message=messaging.Message(notification=messaging.Notification(title='$GOOG up 1.43% on the day',body='$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.',),condition=condition,)# Send a message to devices subscribed to the combination of topics# specified by the provided condition.response=messaging.send(message)# Response is a message ID string.print('Successfully sent message:',response)
// Define a condition which will send to devices which are subscribed// to either the Google stock or the tech industry topics.condition:="'stock-GOOG' in topics || 'industry-tech' in topics"// See documentation on defining a message payload.message:=&messaging.Message{Data:map[string]string{"score":"850","time":"2:45",},Condition:condition,}// Send a message to devices subscribed to the combination of topics// specified by the provided condition.response,err:=client.Send(ctx,message)iferr!=nil{log.Fatalln(err)}// Response is a message ID string.fmt.Println("Successfully sent message:",response)
// Define a condition which will send to devices which are subscribed// to either the Google stock or the tech industry topics.varcondition="'stock-GOOG' in topics || 'industry-tech' in topics";// See documentation on defining a message payload.varmessage=newMessage(){Notification=newNotification(){Title="$GOOG up 1.43% on the day",Body="$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.",},Condition=condition,};// Send a message to devices subscribed to the combination of topics// specified by the provided condition.stringresponse=awaitFirebaseMessaging.DefaultInstance.SendAsync(message);// Response is a message ID string.Console.WriteLine("Successfully sent message: "+response);
REST
POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1
Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA
{
"message":{
"condition": "'dogs' in topics || 'cats' in topics",
"notification" : {
"body" : "This is a Firebase Cloud Messaging Topic Message!",
"title" : "FCM Message",
}
}
}
cURL command:
curl -X POST -H "Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA" -H "Content-Type: application/json" -d '{
"notification": {
"title": "FCM Message",
"body": "This is a Firebase Cloud Messaging Topic Message!",
},
"condition": "'dogs' in topics || 'cats' in topics"
}' https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1
[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2025-09-05 UTC."],[],[],null,[]]