This guide describes how to set up Firebase Cloud Messaging in your mobile and web client apps so that you can reliably receive messages.
To receive messages, use a service that extends
FirebaseMessagingService
.
Your service should override the onMessageReceived
and onDeletedMessages
callbacks.
onMessageReceived
is 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. |
For more information about message types, see Notifications and data messages.
The onMessageReceived
callback is given timeouts that enable you to
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 should schedule a WorkManager
job or
follow the WakeLock guidance
below. In some cases, the
time window for handling a message may be shorter than 10 seconds depending on
delays incurred ahead of calling onMessageReceived
, including OS delays, app
startup time, the main thread being blocked by other operations, or previous
onMessageReceived
calls taking too long. After that timer expires, your app
may be subject to process
killing
or background 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 use FirebaseMessagingService
, you need to add the following in your app
manifest:
<service
android:name=".java.MyFirebaseMessagingService"
android:exported="false">
<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 and uses the custom default icon for
- All notification messages sent from the Notifications composer.
- Any notification message that does not explicitly set the icon 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:
Kotlin
override fun 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.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 job if (needsToBeScheduled()) { // For long-running tasks (10 seconds or more) use WorkManager. scheduleJob() } else { // Handle message within 10 seconds handleNow() } } // 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. }
Java
@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 WorkManager. 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. }
Keep the device awake while handling FCM messages
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 the onMessageReceived
default 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 shouldn't 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).
<uses-permission android:name="android.permission.WAKE_LOCK" />
Then your app will need to acquire a WakeLock at the start of the
FirebaseMessagingService.onMessageReceived()
callback and release it at the
end of the callback.
App's custom FirebaseMessagingService
:
@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();
}
}
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.
For insight into message delivery to your app, see
the
FCM reporting 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.