Firebase Notifications in Background & Foreground in Android

When developers need to integrate push notifications in their Android apps, the first thing that comes to mind is Firebase Notifications or Firebase Messaging. But when they integrate, there are a few common issues that they stuck into. One of those common issues is that the applications don’t get push notifications. There are multiple reasons such as Unregistered device token or app is killed or Android Oreo’s doze mode is on etc.

We will talk about this here for one reason of app being in background and foreground and see how it can be solved. Firebase Notifications applies different mechanisms when the app is foreground and when the app is in the background.

Handling Notifications in Foreground

When the app is closed, your notifications are processed by the Google Service process, which take care of displaying your notifications as required, including the default click action (opening the app) and the notification icon.

When the app is in foreground, the received messages are processed by the app, and since there’s no logic to handle it, nothing will happen!

To fix this, we need our own FirebaseMessagingService, let’s create one. Create a new class that extends it and implement the onMessageReceived method. Then get the Notification object from the remoteMessage and create your own Android Notification.

public class NotificationService extends FirebaseMessagingService { 
  @Override
  public void onMessageReceived(RemoteMessage remoteMessage) {
    super.onMessageReceived(remoteMessage);
    Notification notification = new NotificationCompat.Builder(this)
      .setContentTitle(remoteMessage.getNotification().getTitle())
      .setContentText(remoteMessage.getNotification().getBody())
      .setSmallIcon(R.mipmap.ic_launcher)
      .build();
     NotificationManagerCompat manager = NotificationManagerCompat.from(getApplicationContext());
     manager.notify(123, notification);
   }
}

Then add the Service in your AndroidManifest.xml

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

Now if you try again, you will display notifications while your app is in the foreground!

In real life, your onMessageReceived content will be slightly more complex, you will want different smart actions depending on the type of notification, you will want to show a nicer large icon (the one that appears on the notification body) and for sure to change the status bar icon.

The problem you have now is that your onMessageReceived is ONLY called when the app is in the foreground, if your app if is in the background, Google Services will take care of displaying your message.

The solution? don’t use the “notification” message payload and use “data” instead.

Using Data

The last step to solve this foreground/background problem is to ditch the notification object from your message payload.

Rather than sending:

{
   "to": "the device token"
   "notification":{
     "title":"New Notification!",
     "body":"Test"
   },
   "priority":10
 }

Send:

{
   "to": "the device token"
   "data":{
     "title":"New Notification!",
     "body":"Test"
   },
   "priority":10
 }

This way, your notifications will be always handled by the app, by your NotificationService, and not by the Google Service process.

You will need to change your code as well to handle the data payload:


    // Old Way
    //.setContentTitle(remoteMessage.getNotification().get(“title”))
    //.setContentText(remoteMessage.getNotification().get(“body”))

    // New Way
    .setContentTitle(remoteMessage.getData().get(“title”))
    .setContentText(remoteMessage.getData().get(“body”))

Your whole NotificationService will look like this now:

public class NotificationService extends FirebaseMessagingService { 
  @Override
  public void onMessageReceived(RemoteMessage remoteMessage) {
    super.onMessageReceived(remoteMessage);
    Notification notification = new NotificationCompat.Builder(this)
      .setContentTitle(remoteMessage.getData().get(“title”))
      .setContentText(remoteMessage.getData().get(“body”))
      .setSmallIcon(R.mipmap.ic_launcher)
      .build();
     NotificationManagerCompat manager = NotificationManagerCompat.from(getApplicationContext());
     manager.notify(123, notification);
   }
}

One more advantage of using data instead of notification object is that you can put anything you want in the “data” object. For example a user ID, a URL to an image… any information that you might want to use to build the notification or to pass to the click action. Note that all will be treated as Strings. For example,

"data":{
     "title":"New Notification!",
     "body":"Test",
     "user_id": "1234",
     "avatar_url": "http://www.example.com/avatar.jpg"
 },

Conclusion

Note that if you keep the notification object in your payload, it will behave just like before. You need to get rid of the “notification and only provide the data object.