Receive messages in an Android app

Firebase notifications behave differently depending on the foreground/background state of the receiving app. If you want foregrounded apps to receive notification messages or data messages, you’ll need to write code to handle the onMessageReceived callback. For an explanation of the difference between notification and data messages, see Message types .

Handling 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 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 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>  
 
 . 
 xml 

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 
 / 
 6 
 BKBk7 
 ) 
  
 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 composer .
  • 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 composer .
  • 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:

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 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).

<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.

Receive FCM messages in direct boot mode

Developers who want to send FCM messages to apps even before the device is unlocked can enable an Android app to receive messages when the device is in direct boot mode. For instance, you might want users of your app to receive alarm notifications even on a locked device.

When building out this use case, observe the general best practices and restrictions for direct boot mode . It's particularly important to consider the visibility of direct boot-enabled messages; any user with access to the device can view these messages without entering user credentials.

Prerequisites

  • The device must be set up for direct boot mode.
  • The device must have a recent version of Google Play services installed (19.0.54 or later).
  • The app must be using the FCM SDK ( com.google.firebase:firebase-messaging ) to receive FCM messages.

Enable direct boot mode message handling in your app

  1. In the app-level Gradle file, add a dependency on the FCM direct boot support library:

      implementation 
      
     ' 
     com 
     . 
     google 
     . 
     firebase 
     : 
     firebase 
     - 
     messaging 
     - 
     directboot 
     : 
     20.2.0 
     ' 
     
    
  2. Make the app's FirebaseMessagingService direct boot aware by adding the android:directBootAware="true" attribute in the app manifest:

     < service 
      
     android 
     : 
     name 
     = 
     ".java.MyFirebaseMessagingService" 
      
     android 
     : 
     exported 
     = 
     "false" 
      
     android 
     : 
     directBootAware 
     = 
     "true" 
    >  
    < intent 
     - 
     filter 
    >  
    < action 
      
     android 
     : 
     name 
     = 
     "com.google.firebase.MESSAGING_EVENT" 
      
     / 
    >  
    < / 
     intent 
     - 
     filter 
    >
    < / 
     service 
    > 
    

It is important to ensure that this FirebaseMessagingService can run in direct boot mode. Check for the following requirements:

  • The service should not access credential protected storage while running in direct boot mode.
  • The service should not attempt to use components, such as Activities , BroadcastReceivers , or other Services that are not marked as direct boot aware while running in direct boot mode.
  • Any libraries that the service uses must also not access credential protected storage nor call non-directBootAware components while running in direct boot mode. This means that any libraries the app uses that are called from the service either will need to be direct boot aware, or the app will need to check if it’s running in direct boot mode and not call them in that mode. For example, Firebase SDKs work with direct boot (they can be included in an app without crashing it in direct boot mode), but many Firebase APIs do not support being called in direct boot mode.
  • If the app is using a custom Application , the Application will also need to be direct boot aware (no access to credential protected storage in direct boot mode).

For guidance on sending messages to devices in direct boot mode, see Send direct boot-enabled messages .

Design a Mobile Site
View Site in Mobile | Classic
Share by: