Secrets of Firebase Cloud Messaging — Android (with app in Foreground and background)

Hiten Sahai Bahri
Sep 5, 2018 · 7 min read

Firebase Cloud Messaging (FCM) is a cross-platform messaging solution that lets you reliably deliver messages at no cost.

To read more about the service, its capabilities etc, I recommend you to read it from the official site:

In order to set up your project with the firebase console, follow the steps below:

To set up the firebase cloud messaging service for Android, kindly follow the steps in the below mentioned URL:

Now coming back to the topic that I want to discuss here, is the different scenarios surrounding the application state when receiving cloud messages:

  1. Foreground — When the app is open and running (currently in use)
  2. Background — When the app is open and not in use.(Is in task manager)
  3. Closed — The app is not open at all.

How the android client receives the message:

Every inbound message from the server is routed through a service class that extends FirebaseMessagingService

The messages are captured by the overridden method of the service namely onMessageReceived(). The message received is a RemoteMessage object as shown below.

public class MyAppFirebaseMessagingService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {

}
}

There are certain conditions that needs to be satisfied in order for onMessageReceived method to be triggered. If they aren’t, unfortunately, onMessageReceived() wont even be triggered. Furthermore, in order to understand the what these conditions are, we need to understand the structure of the payload first…

{
“to”: “Unique FCM Token” , -- Who you are sending the message to

Now lets say that you are ready with all the above setups and you plan on sending the above data to the specific android client by referencing the “to” param,

Case1: App is in Foreground —

If the app is in foreground, the message will be received in the onMessageReceived method but you won’t see the push notification in the system tray and if there is any action associated it will simply trigger that.

Sample Code for the same:

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
if (remoteMessage.getNotification() != null && remoteMessage.getNotification().getClickAction() != null) {
startActivity(remoteMessage.getNotification().getClickAction(), null, this);
}
}

There could be some more interesting ways as well but this is just one of them where based on the click action we tell the app what activity to launch.

Case 2: When the app is in background

When the app is in background or is in closed state, there will be nothing received in the onMessageReceived but there will be a notification that will appear on the system tray. The limitation in this case is that you cannot open another activity/fragment or any custom method when the user clicks the notification. Moreover, there is nothing that can be added to the notification other than message title or icon.

Case 3: when the app is closed

Same as when the app is in background.

So this summarises that the cloud messaging works only when the app is in foreground, which ironically means when the user is actually using the app and that is the least time that you want to send a notification since you are already in the app.

So how could we solve this issue for case 2 and 3.

Better Approach — Secret Revealed

Approach 1 — Remove notification parameter from payload

To achieve the the better way of receiving message we are going to remove the notification parameter from the payload so that they only contain data parameter like below:

{
“to”: “Unique FCM Token” , -- Who you are sending the message to

You might be wondering, what difference does it make?
This is the secret of cloud messaging… — “When the payload of the message does not contain notification parameter, everything is captured by the onMessageReceived() method in all application states”

So the sample implementation for data payload is as below:

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Map<String, String> remoteData = remoteMessage.getData();
if (remoteData != null && remoteData.size() > 0) {
createNotification(remoteMessage);
}
}

So the remoteData variable has the data in key value pair structure and you can possible pass any custom data and most importantly ,can build notifications manually using custom properties, colors etc.

Approach 2 — including click_action parameter in notification payload.

In case your payload has to have both the notification and data param and you want to open your app and perform a specific action, set click_action in the notification payload and map it to an intent filter in the Activity you want to launch. For example :

{
“to”: “Unique FCM Token” , -- Who you are sending the message to

here, I have specified click_action in the notification payload and map it to an intent filter in the Activity we want to launch as follows:

<activity android:name=".LaunchActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="general"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>

Now in the activity if I do the following in the onCreate method, I can get the data payload and do anything with it:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_launch);

Intent intent = getIntent();
if(intent.getExtras() != null && intent.getExtras().containsKey("custom_key_1")) {
String data = intent.getExtras().getString("custom_key_1"));
}
}

That’s it. So whatever approach applies best to you, you can do that in your app.

Testing the app by sending the payloads

if you want to test your app by sending the payloads mention above, you can easily do that. All you have to do is to make use of any tool that can perform HTTP POST Request. In my case, I am using the Postman Chrome plugin tool that can be easily installed.

Before making a HTTP Post Request for FCM, you have to consider three things:

1. HTTP Post Request URL : https://fcm.googleapis.com/fcm/send

2. Request Headers :

i). Content-Type : application/json

ii). Authorization : key = YOUR_SERVER_KEY

Server key can be found under Firebase Console -> Your project Name -> settings -> — under cloud messaging -> Server Key

This is how your header will look like under postman. Please note that under the Authorization parameter value — when you paste your server key, prefix it with key=YOUR_SERVER_KEY else you will keep receiving a 401 unauthorised access.

3). Body : In this we are going to have Data payload only in JSON format.

Now with this payload you can test your app in all application states.

You can even add notification payload only and test the behaviour as explained…

This is it for now. If you have any doubts on setup or anything please do let me know in the comments or can contact me on linkedIn as well

Cheers.

Hiten Sahai Bahri

Written by

Lead Android developer at Sportsmate Technologies in Melbourne, Australia. Passionate about trending tech, innovations. Sports and Music lover. Cricket crazy.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade