How to handle notifications with FCM when app is in either foreground or background

how to handle notification when app in foreground in firebase android
handle push notification when app is in background android
how to handle notification when app in background in firebase ios
firebase onmessagereceived not called when app in background
android notification when app is killed
fcm high priority data message
fcm app background
how to handle notification sound when app in background in firebase android

I used firebase to build My project. It will also use the FCM (firebase cloud message). But there is a problem. I can't handle the FCM (create my custom notificaion) when app is in background. The official site tutorial said that case 1: App foreground -> override the "onMessageReceived()" to create your custom notification. case 2: App background -> System will create the notification directly. We needn't and can't do anything. Because it doesn't trigger the "onMessageReceived()" in this case.

However if I can do nothing when app is background, I can't create my custom notification. (e.g. After Users click the notification and it will pop up a window to show detail information.)

So how do I handle notifications with FCM when app is in background?

There is a bad news. Google change the Firebase source code in version 'com.google.firebase:firebase-messaging:11.6.0'. handelIntent is "public final void method" now. which means we can't override it . If you want to use the solution, change the version to be "com.google.firebase:firebase-messaging:11.4.2"

Try my way. It can perfectly work on the project build version is Android 6.0 above(api level 23) and I have tried it already.

There is better way than official site tutorial

The official site said that the notification will be created by system when app is in background. So you can't handle it by overriding the "onMessageReceived()". Because the "onMessageReceived()" is only triggered when app is in foreground.

But the truth is not. Actually the notificaions (when app is in background) are created by Firebase Library.

After I traced the firebase library code. I find a better way.

Step 1. Override the "handleIntent()" instead of "onMessageReceived()" in FirebaseMessagingService why: Because the method will be trigger either app is in foreground or the background. So we can handle FCM message and create our custom notifications in both cases.

@Override
public void handleIntent(Intent intent) {
    Log.d( "FCM", "handleIntent ");
}

Step 2. Parse the message from FCM how: If you don't know the format of the message you set. Print it and try to parse it. Here is the basic illustration

Bundle bundle = intent.getExtras();
if (bundle != null) {
    for (String key : bundle.keySet()) {
        Object value = bundle.get(key);
        Log.d("FCM", "Key: " + key + " Value: " + value);
    }
}

Step 2. Remove the notifications created by Firebase library when the app is in background why: We can create our custom notification. But the notification created by Firebase Library will still be there (Actually it created by ""super.handleIntent(intent)"". There is detail explaination below.). Then we'll have two notifcations. That is rather weird. So we have to remove the notificaion created by Firebase Library how (project build level is Android 6.0 above): Recognize the notifications which we want to remove and get the informaion. And use the "notificationManager.cancel()" to remove them.

private void removeFirebaseOrigianlNotificaitons() {

    //check notificationManager is available
    NotificationManager notificationManager = 
        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    if (notificationManager == null )
        return;

    //check api level for getActiveNotifications()
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
        //if your Build version is less than android 6.0
        //we can remove all notifications instead. 
        //notificationManager.cancelAll();
        return;
    }


    //check there are notifications
    StatusBarNotification[] activeNotifications = 
        notificationManager.getActiveNotifications();
    if (activeNotifications == null)
        return;

    //remove all notification created by library(super.handleIntent(intent))
    for (StatusBarNotification tmp : activeNotifications) {
        Log.d("FCM StatusBarNotification", 
            "StatusBarNotification tag/id: " + tmp.getTag() + " / " + tmp.getId());
        String tag = tmp.getTag();
        int id = tmp.getId();

        //trace the library source code, follow the rule to remove it.
        if (tag != null && tag.contains("FCM-Notification"))
            notificationManager.cancel(tag, id);
    }
}

The my whole sample code:

public class MyFirebaseMessagingService extends FirebaseMessagingService {

private static int notificationCount=0;

@Override
public void handleIntent(Intent intent) {
    //add a log, and you'll see the method will be triggered all the time (both foreground and background).
    Log.d( "FCM", "handleIntent");

    //if you don't know the format of your FCM message,
    //just print it out, and you'll know how to parse it
    Bundle bundle = intent.getExtras();
    if (bundle != null) {
        for (String key : bundle.keySet()) {
            Object value = bundle.get(key);
            Log.d("FCM", "Key: " + key + " Value: " + value);
        }
    }

    //the background notification is created by super method
    //but you can't remove the super method. 
    //the super method do other things, not just creating the notification
    super.handleIntent(intent);

    //remove the Notificaitons
    removeFirebaseOrigianlNotificaitons();

    if (bundle ==null)
        return;

    //pares the message
    CloudMsg cloudMsg = parseCloudMsg(bundle);

    //if you want take the data to Activity, set it
    Bundle myBundle = new Bundle();
    myBundle.putSerializable(TYPE_FCM_PLATFORM, cloudMsg);
    Intent myIntent = new Intent(this, NotificationActivity.class);
    myIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    myIntent.putExtras(myBundle);
    PendingIntent pendingIntent = PendingIntent.getActivity(this, notificationCount, myIntent, PendingIntent.FLAG_UPDATE_CURRENT);

    //set the Notification
    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
            .setSmallIcon(R.mipmap.icon)
            .setContentTitle(cloudMsg.getTitle())
            .setContentText(cloudMsg.getMessage())
            .setAutoCancel(true)
            .setContentIntent(pendingIntent);

    NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify(notificationCount++, notificationBuilder.build());
}



/**
 * parse the message which is from FCM
 * @param bundle
 */
private CloudMsg parseCloudMsg(Bundle bundle) {
    String title = null, msg=null;

    //if the message is sent from Firebase platform, the key will be that
    msg = (String) bundle.get("gcm.notification.body");

    if(bundle.containsKey("gcm.notification.title"))
    title = (String) bundle.get("gcm.notification.title");

    //parse your custom message
    String testValue=null;
    testValue =  (String) bundle.get("testKey");

    //package them into a object(CloudMsg is your own structure), it is easy to send to Activity.
    CloudMsg cloudMsg = new CloudMsg(title, msg, testValue);
    return cloudMsg;
}


/**
 * remove the notification created by "super.handleIntent(intent)"
 */
    private void removeFirebaseOrigianlNotificaitons() {

    //check notificationManager is available
    NotificationManager notificationManager = 
        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    if (notificationManager == null )
        return;

    //check api level for getActiveNotifications()
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
        //if your Build version is less than android 6.0
        //we can remove all notifications instead. 
        //notificationManager.cancelAll();
        return;
     }

    //check there are notifications
    StatusBarNotification[] activeNotifications = 
        notificationManager.getActiveNotifications();
    if (activeNotifications == null)
        return;

    //remove all notification created by library(super.handleIntent(intent))
    for (StatusBarNotification tmp : activeNotifications) {
        Log.d("FCM StatusBarNotification", 
            "tag/id: " + tmp.getTag() + " / " + tmp.getId());
        String tag = tmp.getTag();
        int id = tmp.getId();

        //trace the library source code, follow the rule to remove it.
        if (tag != null && tag.contains("FCM-Notification"))
            notificationManager.cancel(tag, id);
    }
}
}

How to handle FCM Push notification when app is in background or , Question 1: Data messages (messages with no notification payload) are handled the same whether the app is in the foreground or background. When your app is in Foreground. Developer needs to handle the notification message in the handler. Post a notification or update app content in the FCM callback. When your app is in Background. Notification delivered to System Tray. If there's a notification posted, your user may tap the notification and open your app.

However if I can do nothing when app is background, I can't create my custom notification. (e.g. After Users click the notification and it will pop up a window to show detail information.)

So how do I handle notifications with FCM when app is in background?

First, you need to create correct message payload that you send to fcm server. Example:

{
  "to": "topic_name",
  "priority": "high",
  "data": {
    "field1": "field1 value" 
    "field2": "field2 value" 
  }

  "notification" : {
      "body" : "Lorem ipsum",
      "title" : "sampke title" 
      "click_action": "SHOW_DETAILS" 
    }
}

data payload is actual data you want to show as message details after user clicks on notification, notification payload represents how generated notification should look (there are much more attributes possible to set), you don't need to build notification by yourself, you only need to set it properties here.

To show your activity after user taps on notication, you need to set intent filter corresponding to click_action:

<intent-filter>
     <action android:name="SHOW_DETAILS"/>
     <category android:name="android.intent.category.DEFAULT"/>
 </intent-filter>

so activity that have above intent filter will be launched automatically when user taps to notification. Last step is to retrieve data when activity is launched after notification tap. It's pretty easy. Custom data is passed to activity via bundle. Inside onCreate method for your activity do something like that:

Bundle bundle = getIntent().getExtras();
if(bundle.getString("action").equals("SHOW_DETAILS")) /*This indicates activity is launched from notification, not directly*/
{
 //Data retrieved from notification payload send 
 String filed1 = bundle.getString("field1");
 String filed2 = bundle.getString("field2");
}

All of above is valid if app is not running or it's in background. If your app is foreground, no notification will be created. Instead, you will receive onMessageReceived() event so you can handle the same data there (I guess you know how).

Reference:

https://firebase.google.com/docs/cloud-messaging/http-server-ref https://github.com/firebase/quickstart-android/tree/master/messaging

Receive messages in an Android app, It should handle any message within 20 seconds of receipt (10 seconds on Android Marshmallow). The time Notification messages delivered when your app is in the background. In this Foreground, onMessageReceived, onMessageReceived, onMessageReceived TODO(developer): Handle FCM messages here. Firebase Notifications applies different mechanisms when app is foreground and when app is in 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.

You need to use FCM data messages in order to create custom notification in a android app.Even your app is in background, onMessageReceived will be called, so you can process the data and show the custom notification.

https://firebase.google.com/docs/cloud-messaging/android/receive

Data message format which has to be sent from server:

{"message":{
"token":"Your Device Token",
"data":{
  "Nick" : "Mario",
  "body" : "great match!",
  "Room" : "PortugalVSDenmark"
}
}
}

Receive messages in an iOS app, Interpreting notification message payload; Handle messages with method your client app is installed on a device, it can receive messages through the FCM your app is in the background, iOS directs messages with the notification key to the UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any​]) { If you are sending push notification from firebase console & your app is in foreground then MyFirebaseMessagingService will get called. If you are sending push notification from your server(I mean back-end i.e a website or some web based dashboard) & your app is in background or kill or in foreground then MyFirebaseMessagingService will get called.

FCM Won't send a background notification if your app is killed any more, and as you described in your answer about the handleIntent() solution It may work for some devices and for some old version of the FCM, also if you @override method that doesn't described in the official doc's of firebase you may struggle some problems here, and you use it on your own risk!.

What is the solution?

You need to use your own push-notification-service beside FCM like Telegram.

OR using SyncAdapter beside GCM like Gmail.

So if you need it to work successfully like those apps, you have to use your own hack.

Firebase Notifications in Background & Foreground in Android, Firebase Notifications applies different mechanisms when app is foreground and when app is in background. Handling Notifications in Foreground For example a user ID, a URL to an image… any information that you might  How we achieved both foreground and background notification using Angular Progressive Web App and Firebase Cloud Messaging at Kabbage. (FCM) We wanted to build a upgrade, in app/off app

Handle FCM messages on Android, When your app is in the foreground, the FCM notification message is if you need to handle the notification in the client app, whether to customize the on background work complete, update the notification if still active } }. Messages with both notification and data payloads: When in the background, apps receive the notification payload in the notification tray, and only handle the data payload when the user taps on the notification. When in the foreground, your app receives a message object with both payloads available.

Notifying your users with FCM, Notifications are a powerful channel you can use to keep your app's users You can find out more about FCM messages and message handling in this FCM messages and when app is in either foreground or background. a) If the App is in the foreground AND you send just a notification, all the data will be available in "onMessageReceived" ONLY (there's no DATA part!) b) If the App is in the Background AND you send both (notification AND data) the notification will appear automatically in the system tray (also known as THE NOTIFICATION but without sound, etc.).

Beams, You can implement a service to handle incoming notifications. It will get called when your application is: In the foreground; In the background, and the notification only contains a data RemoteMessage; import com.pusher.​pushnotifications.fcm. in your activity when a notification arrives // with your app in the foreground. Question 1: How to handle FCM Push notification when app is in background or killed in Android? I implemented FCM push notifications, i am getting data as well notification data like remoteMessage.getNotification() and remoteMessage.getData().size() > 0. Now, i am able to get data from both when app is in foreground.

Comments
  • Do you have a question, or are you just saying how you chose to handle messages in your own app?
  • @Doug Stevenson I have the problem before. then I got the answer after I traced the Firebase Library code.
  • Hi Leon. You could actually post a question like post and your actual answer. The way it is right now, it might confuse some community members.
  • @LeonChang check this, it will solve your problem:stackoverflow.com/questions/48301350/…. If it does tell me so we can mark it as a duplicate, thank you!
  • @LeonChang you can send a notification in the background and the content of the notification will be written there also (means you do not need to click it to open the app and see the content) , check the link above to know how.
  • This solution is not good. If your app is not running, you will not receive antyhing. This is why Google has removed it from api, it's not needed. Correct way is to use firebase notication (as I described below) or sync adapter/background service.
  • Maybe you can trace the library source ( handleIntent()). The firebase notication you mention is also create by the super.handleIntent(). So if the "handleIntent()" isn't triggered when app is in backgournd. how do firebase library create the "firebase notication"
  • I guess it's handled by Google Play Services, not your app itself
  • How about that? do me a favor. Just do a simple test. you override handleIntent() but do not call super.handleIntent() and try to send your FCM. If you have time, reply it after you finish the test.
  • This is a clever idea. However it looks like in 17.x.x version of firebase-messaging library, FirebaseMessagingService no longer has handleIntent()?
  • Thank for explanations. but how do you handle it when you send a cloud message with FCM console? creating a web UI to let controller send it?
  • AFAIK you can't set click action with Firebase Console. To send message from your computer, I recommend curl