How to handle notification when app in background in Firebase
Asked Answered
B

31

573

Here is my manifest:

<service android:name=".fcm.PshycoFirebaseMessagingServices">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

<service android:name=".fcm.PshycoFirebaseInstanceIDService">
    <intent-filter>
        <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
    </intent-filter>
</service>

When the app is in the background and a notification arrives, then the default notification comes and doesn't run my code of onMessageReceived.

Here is my onMessageReceived code. This is invoked if my app is running on the foreground, not when it is running in the background. How can I run this code when the app is in background too?

// [START receive_message]
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
    // TODO(developer): Handle FCM messages here.
    // If the application is in the foreground handle both data and notification messages here.
    // 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.
    data = remoteMessage.getData();
    String title = remoteMessage.getNotification().getTitle();
    String message = remoteMessage.getNotification().getBody();
    String imageUrl = (String) data.get("image");
    String action = (String) data.get("action");
    Log.i(TAG, "onMessageReceived: title : "+title);
    Log.i(TAG, "onMessageReceived: message : "+message);
    Log.i(TAG, "onMessageReceived: imageUrl : "+imageUrl);
    Log.i(TAG, "onMessageReceived: action : "+action);

    if (imageUrl == null) {
        sendNotification(title,message,action);
    } else {
        new BigPictureNotification(this,title,message,imageUrl,action);
    }
}
// [END receive_message]
Berm answered 8/6, 2016 at 19:18 Comment(4)
It's written in the override sample of onMessageReceived(), the second comment line says Not getting messages here? See why this may be: goo.gl/39bRNJ . The solution, like the below answers, can be found in the documentation in Messages with both notification and data payloadsRakish
Shortly speaking, to wake your killed app, you should always send notification with data object to call your notification service class handler FirebaseMessagingService.onMessageReceived() in your application. Also try sending it not from Firebase console, but to post it from somewhere else (e.g. online testing post service).Organzine
this solution worked for me https://mcmap.net/q/73224/-how-to-handle-notification-when-app-in-background-in-firebase hope helps. Good luckPoise
what ".fcm." PshycoFirebaseMessagingServices is in your manifest? I'm getting error of class not found.. and didn't find in anywhere what is this first part of the parameter.Cordellcorder
G
868

1. Why is this happening?

There are two types of messages in FCM (Firebase Cloud Messaging):

  1. Display Messages: These messages trigger the onMessageReceived() callback only when your app is in foreground
  2. Data Messages: Theses messages trigger the onMessageReceived() callback even if your app is in foreground/background/killed

NOTE: Firebase team have not developed a UI to send data-messages to your devices, yet. You should use your server for sending this type!



2. How to?

To achieve this, you have to perform a POST request to the following URL:

POST https://fcm.googleapis.com/fcm/send

Headers

  • Key: Content-Type, Value: application/json
  • Key: Authorization, Value: key=<your-server-key>

Body using topics

{
    "to": "/topics/my_topic",
    "data": {
        "my_custom_key": "my_custom_value",
        "my_custom_key2": true
     }
}

Or if you want to send it to specific devices

{
    "data": {
        "my_custom_key": "my_custom_value",
        "my_custom_key2": true
     },
    "registration_ids": ["{device-token}","{device2-token}","{device3-token}"]
}


NOTE: Be sure you're not adding JSON key notification
NOTE: To get your server key, you can find it in the firebase console: Your project -> settings -> Project settings -> Cloud messaging -> Server Key

3. How to handle the push notification message?

This is how you handle the received message:

@Override
public void onMessageReceived(RemoteMessage remoteMessage) { 
     Map<String, String> data = remoteMessage.getData();
     String myCustomKey = data.get("my_custom_key");

     // Manage data
}
Garrett answered 15/6, 2016 at 20:28 Comment(24)
Hi, apparently if I supply the to key, it works. But when I replace that with a list of device tokens that I wish to send to, aka registration_ids, the notification does not get through. Any ideas? EDIT: I solved it. For some reason, if I remove the sender-id key from my headers, everything works normally. Strange.Bluster
You can send "data" and "notification" keys in the same notification following these steps https://mcmap.net/q/73224/-how-to-handle-notification-when-app-in-background-in-firebase :)Gennygeno
If you are sending messages to a Topic, please make sure you have subscribed to the topic on your service.Howbeit
If the app is in Background, and you press the notification, it automatically opens the launcher activity, How can you hand the data?Myeloid
The app in the background with notification payload is the dumbest thing ever. Is there a practical use for this? 99% the notifications would be sent when your app is in the background to get the user back into your app.Ramage
You're right, @Codeversed. Maybe if there's few business cases in which you want to send notifications, like exclusive promotions or settings to online users. But definitely there's a small portion of uses...Garrett
I'm using push notifications in Ionic 2 with phonegap plugin and finally I can manage notifications even when the app is killed/bg, thank you very much. I tried to use the field "to" using just 1 device_id and it works, so it's ok also if it's not a topic.Manmade
Firebase team have not developed a UI to send data-messages to your devices, yet. Has this changed in the past year?Galarza
@Garrett In oreo when app killed, onMessageReceived is not getting called. i just have payload with data only. any update do you have ?Vertu
@SamirMangroliya no, sorry. No update yet. Have you tried using one of the sample apps? Does it work?Garrett
@Garrett Yes i checked with sample apps but in OREO , its not working.Vertu
@SamirMangroliya I recommend submitting the error to Google. Sorry for not helping :(Garrett
When app is in background onMessageReceived does not get called and it's a serious issue in FCM!!! Also please update your answer.Mcatee
Well, for me, it seems like this happens on certain android devices. Spent hours thinking that it was an actual issue, but then turned out to be nothing. So my advice is to test it on different devices. I even tested it on a VM and i got the notification even when the app was killed. I am just saying this to save someone's time.Abduce
In order to send message using a topic method, make sure you subscribe your app to your topic. by adding: FirebaseMessaging.getInstance().subscribeToTopic("your_topic");Teleplay
no longer works The request was missing an Authentication Key (FCM Token). Please, refer to section "Authentication" of the FCM documentation, at firebase.google.com/docs/cloud-messaging/server. Error 401Nth
If your are using postman don't use Authorization section, use Header key/value section instead, don't forget "key=" suffix in your value key server: . #45310174Arron
Display Messages have callback too from background.Sailmaker
For those strugling to send single broadcast message to both android and ios device, (because andoid must NOT use "notification" param while ios MUST use it), I suggest you use different topics for android and ios. That way, you can put "notification" on broadcast sent to ios's topic and not include "notification" param on android's topicEudemonia
Three years later, and this is still an issue: if your app is running in the background, onReceiveMessage will not be called if your message has a "notification" key, regardless if it has a "data" key. Since your server cannot determine whether or not the app is running in the background, it needs to send two messages. RidiculousEccentric
The authorization header value will be “Bearer “+yourTokenDraftee
From FCM Doc: "Notification message: FCM automatically displays the message to end-user devices on behalf of the client app." So, you need data inside "notification" key to allow FCM to displays the message when app is closed. Notification Message example: { "message":{ "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...", "notification":{ "title":"Portugal vs. Denmark", "body":"great match!" } } }Dinsmore
5 years passed and we still can't deal background FCM with onMessageReceived, nor have a simple UI in FCM console. Which means it takes more time and backend engineers to develop a feature like in-app notification center for users and a console for marketing members. How come Firebase insists on NOT fixing it?Middlebrooks
I am using a data payload with the notification object, yet it does not trigger the onmessage received method no matter app is in background or foreground. I don't know if anything exist by which I can listenOverrate
M
215

To make firebase library to call your onMessageReceived() in the following cases

  1. App in foreground
  2. App in background
  3. App has been killed

you must not put JSON key notification in your request to Firebase API but instead, use data, see below.

The following message will not call your onMessageReceived() when your app is in the background or killed, and you can't customize your notification.

{
   "to": "/topics/journal",
   "notification": {
       "title" : "title",
       "text": "data!",
       "icon": "ic_notification"
    }
}

but instead using this will work

{
  "to": "/topics/dev_journal",
   "data": {
       "text":"text",
       "title":"",
       "line1":"Journal",
       "line2":"刊物"
   }
} 

Basically, the message is sent in the argument RemoteMessage along with your data object as Map<String, String>, then you can manage the notification in onMessageReceived as in the snippet here

@Override
public void onMessageReceived(RemoteMessage remoteMessage) { 
     Map<String, String> data = remoteMessage.getData();

     //you can get your text message here.
     String text= data.get("text");


     NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
        // optional, this is to make beautiful icon
             .setLargeIcon(BitmapFactory.decodeResource(
                                    getResources(), R.mipmap.ic_launcher))  
        .setSmallIcon(smallIcon)  //mandatory
      .......
    /*You can read more on notification here:
    https://developer.android.com/training/notify-user/build-notification.html
    https://www.youtube.com/watch?v=-iog_fmm6mE
    */
}
Mateo answered 5/8, 2016 at 18:15 Comment(16)
Is it possible to achieve this from Firebase console?Tonicity
@koder, unfortunately Firebase console doesn't support this, you have to use tool to send post message request to firebase such as curl or postman(chrome plugin), firebase messaging api please refer to document here - firebase.google.com/docs/cloud-messaging/http-server-refMateo
The notification object should not contain text rather bodyMoneymaker
this is what I was searching for more than a day..... Thank you so much it worked perfectly. and it would be better if u explain how the key vale pairs in the data can be handled in onMessageReceived() to start a activity with those values.Elvia
Your method works fine when the app is in background, however when the app is killed I am not receiving dataHatten
Same problem of Sanzhar. If the app is killed i do not receive any message.Polash
There is no solution/answer when the app is killed. Refer to this 'answer'Entopic
It is not possible to set priority of data message to high, which is equals to not receiving messages when phone is in doze mode. It receives the message when it is waking up.Covert
this works when it's in the background or killed, but if you force kill your app from the settings it won't work, but its working really good and I think is the best solution.Kristine
if you have your own backend team, tells them to also use custom data payloads so it is available on this state: App in foreground, App in background, App has been killedPennington
Thank you for answering this. I was using "data" instead of notification and was scratching my head when I was receiving notifications when my app is opened or in the foreground. I only want to see notifications when my app is in the backgroundImprecise
In your AndroidManifest.xml file remove android:exported=false from your Messaging service.Ponytail
From FCM Doc: "Notification message: FCM automatically displays the message to end-user devices on behalf of the client app." So, you need data inside "notification" key to allow FCM to displays the message when app is closed. Notification Message example: { "message":{ "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...", "notification":{ "title":"Portugal vs. Denmark", "body":"great match!" } } }Dinsmore
Hi can you please elaborate? I am currently sending a data only payload to my ionic application that is using FCM. It works fine if app is in background/foreground. But doesnt work when application is closed. What is the event that can help me detect when the data payload is received on the app when it is closed?Mediant
For anyone still facing an issue with this, ensure that you're notification body/payload doesn't have a notification in it, otherwise it won't work -> refer to this - https://mcmap.net/q/74321/-when-app-is-in-background-or-killed-fcm-onmessagereceived-not-calledCrowbar
I tried data only messages, they are never delivered to androidOverrate
G
160

I feel like all the responses are incomplete but all of them have something that you need to process a notification that has data when your app is in the background.

Follow these steps and you will be able to process your notifications when your app is in the background.

  1. Add an intent-filter like this:

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

to an activity that you want to process the notification data.

  1. Send notifications with the next format:

     { 
      "notification" : {
             "click_action" : ".MainActivity", 
             "body" : "new Symulti update !", 
             "title" : "new Symulti update !", 
             "icon" : "ic_notif_symulti" }, 
      "data": { ... },
      "to" : "c9Vaa3ReGdk:APA91bH-AuXgg3lDN2WMcBrNhJZoFtYF9" }
    

The key here is add

"click_action" : ".MainActivity"

where .MainActivity is the activity with the intent-filter that you added in step 1.

  1. Get data info from notification in the onCreate of .MainActivity:

     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         //get notification data info
         Bundle bundle = getIntent().getExtras();
         if (bundle != null) {
            //bundle must contain all info sent in "data" field of the notification
         }
     }
    

And that should be all you need to do.

Gennygeno answered 16/2, 2017 at 16:16 Comment(15)
This should be the correct answer. None of the docs say anything about requiring a click_action AND an Intent Filter for the notification to even show up in the tray. They are both required.Ovenware
@Ovenware they are not required for the notification to show upAguascalientes
I have both data and notification blocks but i am not able to receive data in activity?Neurology
have you push notifications working OK in your app? I am not sure how to help you without more information.Gennygeno
Hey there @DanielS. ! I have push notifications working for my app but when the app is in the background, they don't work! I've made all the changes that you've given in this answer. Can you help? Thank you :)Combined
I just don't understand how this is not the accepted answer. Without click_action, it just will not work. Saved my dayCharleencharlemagne
Thanks for your support @ArunShankar. The accepted answer was responded 7 months before mine and its a good answer. I can not understand why no one talks about click_action and that's why I have added my answer. I am very happy that it is being very useful for a lot of people and that is what matters at the end of the day :)Gennygeno
Looks like this is only available in the legacy version of the Notification. firebase.google.com/docs/cloud-messaging/…Nuli
If onCreate() of MainActivity is being called, won't it mean that Activity is started?Cosmonaut
Hi, how to send notification like in point 2? Is it possible using Firebase UI? Or should I use postman post call?Indium
When I do this "bundle" is null. It's not receiving anything in the launched activity.Mallorymallow
It seems that there could a potential mistake if we name the intent filter action name the same as the activity name, I named them differently and it worked.. The click_action should reference the intent filter name specifically.Brassiere
Hi can you please elaborate? I am currently sending a data only payload to my ionic application that is using FCM. It works fine if app is in background/foreground. But doesnt work when application is closed. What is the event that can help me detect when the data payload is received on the app when it is closed?Mediant
I just wanted to add that I was facing the same problem, but in my particular case I had an SplashActivity in my app, so I had to move the bundle code reader to my SplashActivity class in order to read the data and also, It was not neccesary to add a click_action in the payload, so thank you!Wheeled
You don't need "click_action" especially if you only have a "data" payloadZel
K
60

According to the firebase documentation in send downstream using firebase, there is 2 type of payload :

  1. data

    This parameter specifies the custom key-value pairs of the message's payload. Client app is responsible for processing data messages. Data messages have only custom key-value pairs.

  2. notification

    This parameter specifies the predefined, user-visible key-value pairs of the notification payload. FCM automatically displays the message to end-user devices on behalf of the client app. Notification messages have a predefined set of user-visible keys.

When you are in the foreground you can get the data inside FCM using onMessageReceived(), you can get your data from data payload.

data = remoteMessage.getData();
String customData = (String) data.get("customData");

When you are in background, FCM will showing notification in system tray based on the info from notification payload. Title, message, and icon which used for the notification on system tray are get from the notification payload.

{
  "notification": {
        "title" : "title",
        "body"  : "body text",
        "icon"  : "ic_notification",
        "click_action" : "OPEN_ACTIVITY_1"
       }
}

This notification payload are used when you want to automactically showing notification on the system tray when your app is in the background. To get notification data when your app in the background, you should add click_action inside notification payload.

If you want to open your app and perform a specific action [while backgrounded], set click_action in the notification payload and map it to an intent filter in the Activity you want to launch. For example, set click_action to OPEN_ACTIVITY_1 to trigger an intent filter like the following:

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

Put that intent-filter on your manifest, inside one of your activity tag. When you click the notification, it will open the app and go straight to activity that you define in click_action, in this case "OPEN_ACTIVTY_1". And inside that activity you can get the data by :

Bundle b = getIntent().getExtras();
String someData = b.getString("someData");

I'm using FCM for my android app and use both of the payload. Here is the example JSON i'm using :

{
  "to": "FCM registration ID",
  "notification": {
    "title" : "title",
    "body"  : "body text",
    "icon"  : "ic_notification",
    "click_action" : "OPEN_ACTIVITY_1"
   },
   "data": {
     "someData"  : "This is some data",
     "someData2" : "etc"
   }
}
Krimmer answered 25/8, 2016 at 15:32 Comment(8)
"Put that intent-filter on your manifest, inside application tag." you can't put intent-filter inside application tag.Remise
your JSON dows not show where the click_action key goes.Shanteshantee
this is not the right answer ? what is click action n where does it go ? some one should down vote or clean thisPyramidon
@KyryloZapylaiev I just correct & update the answer: "Put that intent-filter on your manifest, inside one of your activity tag".Polysaccharide
@Shanteshantee updated and formatted. you can check it againPolysaccharide
@Pyramidon this is not answer the original question yes, but this is very important information that not contained on accepted answer. so I think this answer is worth to be herePolysaccharide
Bundle b = getIntent().getExtras() is null?Mallorymallow
@TheFluffyTRex if it's null, maybe push notification payload did not have data. Can you check again your payload for notification?Krimmer
T
55

Working as of July 2019

Android compileSdkVersion 28, buildToolsVersion 28.0.3 and firebase-messaging:19.0.1

After many many hours of researching through all of the other StackOverflow questions and answers, and trying innumerable outdated solutions, this solution managed to show notifications in these 3 scenarios:

- App is in foreground:
the notification is received by the onMessageReceived method at my MyFirebaseMessagingService class

- App has been killed (it is not running in background): the notification is sent to the notification tray automatically by FCM. When the user touches the notification the app is launched by calling the activity that has android.intent.category.LAUNCHER in the manifest. You can get the data part of the notification by using getIntent().getExtras() at the onCreate() method.

- App is in background: the notification is sent to the notification tray automatically by FCM. When the user touches the notification the app is brought to the foreground by launching the activity that has android.intent.category.LAUNCHER in the manifest. As my app has launchMode="singleTop" in that activity, the onCreate() method is not called because one activity of the same class is already created, instead the onNewIntent() method of that class is called and you get the data part of the notification there by using intent.getExtras().

Steps: 1- If you define your app's main activity like this:

<activity
    android:name=".MainActivity"
    android:label="@string/app_name"
    android:largeHeap="true"
    android:screenOrientation="portrait"
    android:launchMode="singleTop">
    <intent-filter>
        <action android:name=".MainActivity" />
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

2- add these lines at the onCreate() method of your MainActivity.class

Intent i = getIntent();
Bundle extras = i.getExtras();
if (extras != null) {
    for (String key : extras.keySet()) {
        Object value = extras.get(key);
        Log.d(Application.APPTAG, "Extras received at onCreate:  Key: " + key + " Value: " + value);
    }
    String title = extras.getString("title");
    String message = extras.getString("body");
    if (message!=null && message.length()>0) {
        getIntent().removeExtra("body");
        showNotificationInADialog(title, message);
    }
}

and these methods to the same MainActivity.class:

@Override
public void onNewIntent(Intent intent){
    //called when a new intent for this class is created.
    // The main case is when the app was in background, a notification arrives to the tray, and the user touches the notification

    super.onNewIntent(intent);

    Log.d(Application.APPTAG, "onNewIntent - starting");
    Bundle extras = intent.getExtras();
    if (extras != null) {
        for (String key : extras.keySet()) {
            Object value = extras.get(key);
            Log.d(Application.APPTAG, "Extras received at onNewIntent:  Key: " + key + " Value: " + value);
        }
        String title = extras.getString("title");
        String message = extras.getString("body");
        if (message!=null && message.length()>0) {
            getIntent().removeExtra("body");
            showNotificationInADialog(title, message);
        }
    }
}


private void showNotificationInADialog(String title, String message) {

    // show a dialog with the provided title and message
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setTitle(title);
    builder.setMessage(message);
    builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int whichButton) {
            dialog.cancel();
        }
    });
    AlertDialog alert = builder.create();
    alert.show();
}

3- create the class MyFirebase like this:

package com.yourcompany.app;

import android.content.Intent;
import android.util.Log;

import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;

public class MyFirebaseMessagingService extends FirebaseMessagingService {


    public MyFirebaseMessagingService() {
        super();
    }

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {

        Log.d(Application.APPTAG, "myFirebaseMessagingService - onMessageReceived - message: " + remoteMessage);

        Intent dialogIntent = new Intent(this, NotificationActivity.class);
        dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        dialogIntent.putExtra("msg", remoteMessage);
        startActivity(dialogIntent);

    }

}

4- create a new class NotificationActivity.class like this:

package com.yourcompany.app;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ContextThemeWrapper;

import com.google.firebase.messaging.RemoteMessage;

public class NotificationActivity extends AppCompatActivity {

private Activity context;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    context = this;
    Bundle extras = getIntent().getExtras();

    Log.d(Application.APPTAG, "NotificationActivity - onCreate - extras: " + extras);

    if (extras == null) {
        context.finish();
        return;
    }

    RemoteMessage msg = (RemoteMessage) extras.get("msg");

    if (msg == null) {
        context.finish();
        return;
    }

    RemoteMessage.Notification notification = msg.getNotification();

    if (notification == null) {
        context.finish();
        return;
    }

    String dialogMessage;
    try {
        dialogMessage = notification.getBody();
    } catch (Exception e){
        context.finish();
        return;
    }
    String dialogTitle = notification.getTitle();
    if (dialogTitle == null || dialogTitle.length() == 0) {
        dialogTitle = "";
    }

    AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(context, R.style.myDialog));
    builder.setTitle(dialogTitle);
    builder.setMessage(dialogMessage);
    builder.setPositiveButton(getResources().getString(R.string.accept), new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int whichButton) {
            dialog.cancel();
        }
    });
    AlertDialog alert = builder.create();
    alert.show();

}

}

5- Add these lines to your app Manifest, inside your tags

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

    <meta-data android:name="com.google.firebase.messaging.default_notification_channel_id" android:value="@string/default_notification_channel_id"/>

    <activity android:name=".NotificationActivity"
        android:theme="@style/myDialog"> </activity>

    <meta-data
        android:name="com.google.firebase.messaging.default_notification_icon"
        android:resource="@drawable/notification_icon"/>

    <meta-data
        android:name="com.google.firebase.messaging.default_notification_color"
        android:resource="@color/color_accent" />

6- add these lines in your Application.java onCreate() method, or in MainActivity.class onCreate() method:

      // notifications channel creation
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      // Create channel to show notifications.
      String channelId = getResources().getString("default_channel_id");
      String channelName = getResources().getString("General announcements");
      NotificationManager notificationManager = getSystemService(NotificationManager.class);
      notificationManager.createNotificationChannel(new NotificationChannel(channelId,
              channelName, NotificationManager.IMPORTANCE_LOW));
  }

Done.

Now for this to work well in the 3 mentioned scenarios, you have to send the notification from the Firebase web console in the following way:

In the Notification section: Notification Title = Title to display in the notification dialog (optional) Notification text = Message to show to the user (required) Then in the Target section: App = your Android app and in Additional Options section: Android Notification Channel = default_channel_id Custom Data key: title value: (same text here than in the Title field of the Notification section) key: body value: (same text here than in the Message field of the Notification section) key:click_action value: .MainActivity Sound=Disabled
Expires=4 weeks

You can debug it in the Emulator with API 28 with Google Play.

Happy coding!

Thisbe answered 19/7, 2019 at 21:4 Comment(12)
Thank you for this great answer.Forenamed
@Thisbe , If I want to open Url when I receive Notification , How I can Handle thisBindman
not working in vivo opp phone when app is close or killedSumptuous
I haven't tried in a Vivo phone but It is currently working in many other Android phones. Please read slowly every step, check all the details, and then turn on debugging breakpoints at the first line of each of the methods that I've mentioned here, connect a real phone to your development computer with a cable and debug the app while sending a message from FCM. Check that you are sending the FCM messages with all the parameters and the format that I mentioned. Good luck!Thisbe
@Bindman These methods show how to receive the notification and handle it. In this case I just wanted to show the text of the message and the title on a dialog windows, but you can modify the NotificationActivity.class to do whatever you want with the msg received. Instead of creating an AlertDialog.Builder you can extract the URL that you have sent in one of the msg data fields and use it as you need. Happy coding!Thisbe
This is the most up to date answer, as the Firebase documentation now reads: 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. 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. (firebase.google.com/docs/cloud-messaging/android/…) So many old answers are out of dateAubyn
The code is a bit messy, but the solution works. Thanks!Karee
Don't you think that anyone can send arbitrary messages to your launcher activity. And it will display any messages from untrusted source?Estivate
What does app in the background mean? If users swipe close my app,is it still in the background?Sansculotte
@Thisbe hello i have a problem with this can you help me?Eventual
This is the way I got success to send a notification from Firebase Console with a link and opening a page in the browser, even when app is in background, thank you.Tedda
2022, data only messages are not delivered to android. Notification& data messages are delivered but the onmessage received is never called either foreground or backgroundOverrate
H
38

According to docs

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

If 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, set click_action to OPEN_ACTIVITY_1 to trigger an intent filter like the following:

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

Edit :

Based on this thread :

You can't set click_action payload using Firebase Console. You could try testing with a curl command or a custom http server

curl --header "Authorization: key=<YOUR_KEY_GOES_HERE>" 
     --header Content-Type:"application/json" https://fcm.googleapis.com/fcm/send  
     -d "{\"to\":\"/topics/news\",\"notification\": 
         {\"title\": \"Click Action Message\",\"text\": \"Sample message\",
            \"click_action\":\"OPEN_ACTIVITY_1\"}}"
Hiltonhilum answered 8/6, 2016 at 19:23 Comment(5)
you are right. i have read the docs. but i m confused where to put this in my manifest ? i have to run the code on the onMessageReceived on that java file so for that what should i have to do sir ?Berm
You can't make the app automatically call onMessageReveiced when it is on background. Instead you need to handle the intent received and make the handler call it. Or probably it is best to implement separate class/method that is called by both onMessageReveiced and your intent handling. I added handler to onNewIntent of the main activity and it works fine for me.Gulf
too late to respond to @Parath Patel questions but maybe this will helps someone else with the same problems, take a look to my answer here https://mcmap.net/q/73224/-how-to-handle-notification-when-app-in-background-in-firebaseGennygeno
where had to put this action for? for my activity or Firebasemessage service?Blastoff
** on multiple notification redirection is not working? eg: if i have three notification from fcm on background using "click_action" first notification redirected successfully and then 2 notification click the activity redirection is not workingGumboil
A
26

To capture the message in background you need to use a BroadcastReceiver

import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.legacy.content.WakefulBroadcastReceiver
import com.google.firebase.messaging.RemoteMessage

class FirebaseBroadcastReceiver : WakefulBroadcastReceiver() {

    val TAG: String = FirebaseBroadcastReceiver::class.java.simpleName

    override fun onReceive(context: Context, intent: Intent) {

        val dataBundle = intent.extras
        if (dataBundle != null)
            for (key in dataBundle.keySet()) {
                Log.d(TAG, "dataBundle: " + key + " : " + dataBundle.get(key))
            }
        val remoteMessage = RemoteMessage(dataBundle)
        }
    }

and add this to your manifest:

<receiver
      android:name="MY_PACKAGE_NAME.FirebaseBroadcastReceiver"
      android:exported="true"
      android:permission="com.google.android.c2dm.permission.SEND">
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        </intent-filter>
</receiver>
Artamas answered 5/10, 2016 at 7:5 Comment(10)
This really receives the notification message, when the app is in background. But it doesn't stop the default Firebase receiver from handling it, so the message still gets displayed as a notification alert.Geiss
At the moment doens't work, so that's why I'm proposing this solution. There is a filed bug on the google bugbase. You may want to check that out.Artamas
Could you please post a link to the bug hereGeiss
This is clearly not working when the app is killed. I have tried this solution for hours. for some reason the receiver is working while the app is in the background and not working when the app is killedMedicare
Sure, but this is another issue not directly related to this issue XcodeNOOB.Artamas
Getting null.Hyderabad
Try with intent.getExtras()Artamas
still getting nullParable
messages in FCM are sent as strings, suppose you sent data: { "key": 123 } try calling intent.getExtras().getSerializable("key") in javaMashhad
this works like a charm! even in the foreground, background, and when the app is killed. ThanksDawes
S
26

According to the docs: May 17, 2017

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.

So,you should use both of the payload notification + data:

{
  "to": "FCM registration ID",
  "notification": {
    "title" : "title",
    "body"  : "body text",
    "icon"  : "ic_notification"
   },
   "data": {
     "someData"  : "This is some data",
     "someData2" : "etc"
   }
}

There is no need to use click_action.You should just get exras from intent on LAUNCHER activity

<activity android:name=".MainActivity">
        <intent-filter>
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
</activity>

Java code should be on onCreate method on MainActivity :

Intent intent = getIntent();
if (intent != null && intent.getExtras() != null) {
    Bundle extras = intent.getExtras();
    String someData= extras.getString("someData");
    String someData2 = extras.getString("someData2");
}

You can test both of the payload notification + data from Firebase Notifications Console . Don't forget to fill custom data fields on Advanced options section

Shelah answered 24/5, 2017 at 6:44 Comment(1)
this should be the answerShaikh
O
24

Since the display-messages which are sent from Firebase Notification UI only works if your app is in foreground. For data-messages, there is a need to make a POST call to FCM

Steps

  1. Install Advanced Rest Client Google Chrome Extension enter image description here

  2. Add the following headers

    Key: Content-Type, Value: application/json

    Key: Authorization, Value: key="your server key" enter image description here

  3. Add the body

    • If using topics :

      {
          "to" : "/topics/topic_name",
          "data": {
          "key1" : "value1",
          "key2" : "value2",
          }
      }
      
    • If using registration id :

      {
          "registration_ids" : "[{"id"},{id1}]",
          "data": {
          "key1" : "value1",
          "key2" : "value2",
           }
      }
      

Thats it!. Now listen to onMessageReceived callback as usual.

@Override
public void onMessageReceived(RemoteMessage remoteMessage) { 
     Map<String, String> data = remoteMessage.getData();
     String value1 = data.get("key1");
     String value2 = data.get("key2");
}
Ontiveros answered 17/12, 2016 at 18:11 Comment(1)
Hi can you please elaborate? I am currently sending a data only payload to my ionic application that is using FCM. It works fine if app is in background/foreground. But doesnt work when application is closed. What is the event that can help me detect when the data payload is received on the app when it is closed?Mediant
B
24
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {

}

is not called every time it is called only when app is in forground

there is one override method this method is called every time , no matter what app is in foreground or in background or killed but this method is available with this firebase api version

this is the version u have to import from gradle

compile 'com.google.firebase:firebase-messaging:10.2.1'

this is the method

@Override
public void handleIntent(Intent intent) {
    super.handleIntent(intent);

    // you can get ur data here 
    //intent.getExtras().get("your_data_key") 


}

with previous firebase api this method was not there so in that case fire base handle itself when app is in background .... now u have this method what ever u want to do ... u can do it here in this method .....

if you are using previous version than default activity will start in that case u can get data same way

if(getIntent().getExtras() != null && getIntent().getExtras().get("your_data_key") != null) {
String strNotificaiton = getIntent().getExtras().get("your_data_key").toString();

// do what ever u want .... }

generally this is the structure from server we get in notification

{
    "notification": {
        "body": "Cool offers. Get them before expiring!",
        "title": "Flat 80% discount",
        "icon": "appicon",
        "click_action": "activity name" //optional if required.....
    },
    "data": {
        "product_id": 11,
        "product_details": "details.....",
        "other_info": "......."
    }
}

it's up to u how u want to give that data key or u want give notification anything u can give ....... what ever u will give here with same key u will get that data .........

there are few cases if u r not sending click action in that case when u will click on notification default activity will open , but if u want to open your specific activity when app is in background u can call your activity from this on handleIntent method because this is called every time

Brainsick answered 25/5, 2017 at 15:34 Comment(5)
I updated the firebase-messaging to 10.2.1, Added Data to the notification message, and it worked. Foreground, Background, and Killed. ThanksBryan
in Kotlin, i get this Error:(44, 5) 'handleIntent' in 'FirebaseMessagingService' is final and cannot be overriddenWagonette
It is not working above the versions of firebase 11Sooth
handleIntent gets called on com.google.firebase.messaging.NEW_TOKEN action as well. Be careful when using this.Chui
this is the right answer, thank you.Kodok
P
12

Simple summary like this

  • if your app is running;

    onMessageReceived()
    

is triggers.

  • if your app is not running (killed by swiping) ;

    onMessageReceived()
    

is not triggered and delivered by direclty. If you have any specialy key-value pair. They don' t work beacuse of onMessageReceived() not working.

I' ve found this way;

In your launcher activity, put this logic,

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState, R.layout.activity_splash);

    if (getIntent().getExtras() != null && getIntent().getExtras().containsKey("PACKAGE_NAME")) {

        // do what you want

        // and this for killing app if we dont want to start
        android.os.Process.killProcess(android.os.Process.myPid());

    } else {

        //continue to app
    }
}

in this if block, search for your keys according to firebase UI.

In this example my key and value like above; (sorry for language =)) enter image description here

When my code works, i get "com.rda.note".

android.os.Process.killProcess(android.os.Process.myPid());

with this line of code, i closed my application and open Google Play Market

happy coding =)

Profane answered 19/10, 2016 at 11:26 Comment(0)
D
12

2017 updated answer

Here is a clear-cut answer from the docs regarding this:

enter image description here

Definition answered 28/6, 2017 at 20:29 Comment(1)
This is the link you wanted to post firebase.google.com/docs/cloud-messaging/android/receiveDamato
O
9

I figured out the scenarios,

When app is in foreground, onMessageReceived() method is called from the FirebaseService.So the pendingIntent defined in the service class will be called.

And when app is in background, first activity is called.

Now, if you use a splash activity, then must keep in mind the splashactivity will be called, else if there is no splashActivity, then whatever the first activity is, will be called.

Then you need to check getIntent() of the firstActivity to see if it has any bundle .if everything is alright you will see bundle is there with values filled in. If the value in data tag sent from server looks like this,

"data": {
    "user_name": "arefin sajib",
    "value": "user name notification"
  }

Then in the first activity, you will see, there is a valid intent( getIntent() is not null) , valid bundle and inside bundle , there will the whole JSON mentioned above with data as key.

For this scenario, code for extraction of value will look like this,

    if(getIntent()!=null){
            Bundle bundle = getIntent().getExtras();
            if (bundle != null) {
                try {
                   JSONObject object = new JSONObject(bundle.getStringExtra("data"));
String user_name = object.optString("user_name");

                } catch (JSONException e) {
                    e.printStackTrace();
                }


            }
        }
Overrate answered 24/9, 2017 at 13:26 Comment(1)
this really solved my problem. tried many other things. But this really worked very well. ThanksPressurize
P
8

I had same problem. After some digging why my MainActivity is called with intent without data I realized that my LAUNCHER activity (as in Manifest) is SplashActivity. There I found the message data and forwarded them to MainActivity. Works like sharm. I beleive this can help someone.

Thanks for all another answers.

Pouliot answered 17/9, 2020 at 9:49 Comment(2)
Thanks for the suggestion. That worked for me too. My SplashScreen was getting the intent and not available inside MainActivityFalcongentle
Still working solution, thank you !Domingo
M
5

Remove notification payload completely from your server request. Send only data and handle it in onMessageReceived(), otherwise your onMessageReceived will not be triggered when the app is in background or killed.

Here is what I am sending from server:

{
  "data":{
    "id": 1,
    "missedRequests": 5
    "addAnyDataHere": 123
  },
  "to": "fhiT7evmZk8:APA91bFJq7Tkly4BtLRXdYvqHno2vHCRkzpJT8QZy0TlIGs......"
}

So you can receive your data in onMessageReceived(RemoteMessage message) like this: (let's say I have to get the id)

Object obj = message.getData().get("id");
        if (obj != null) {
            int id = Integer.valueOf(obj.toString());
        }

And similarly you can get any data which you have sent from server within onMessageReceived().

Massy answered 17/10, 2016 at 10:37 Comment(1)
Hi can you please elaborate? I am currently sending a data only payload to my ionic application that is using FCM. It works fine if app is in background/foreground. But doesnt work when application is closed. What is the event that can help me detect when the data payload is received on the app when it is closed?Mediant
D
5

Thanks to All of you for your Answers. But I solved this by sending data message instead of sending Notification. Server code

<?php
$url = "https://fcm.googleapis.com/fcm/send";
$token = "C-l6T_a7HouUK****";
$serverKey = "AAAAaOcKS00:********";
define( 'API_ACCESS_KEY', $serverKey );
$registrationIds = array($token);
// prep the bundle

$msg = array

(
 'message'  => 'here is a message. message',
 'title'        => 'This is a title. title',
 'subtitle' => 'This is a subtitle. subtitle',
 'tickerText'   => 'Ticker text here...Ticker text here...Ticker text 
 here',
 'vibrate'  => 1,
 'sound'        => 1,
 'largeIcon'    => 'large_icon',
 'smallIcon'    => 'small_icon'

);

$fields = array

(
  'registration_ids'    => $registrationIds,
  'data'            => $msg

);
$headers = array

(
  'Authorization: key=' . API_ACCESS_KEY,
 'Content-Type: application/json'

);


$ch = curl_init();

curl_setopt( $ch,CURLOPT_URL, 'https://android.googleapis.com/gcm/send' 
);

curl_setopt( $ch,CURLOPT_POST, true );

curl_setopt( $ch,CURLOPT_HTTPHEADER, $headers );

curl_setopt( $ch,CURLOPT_RETURNTRANSFER, true );

curl_setopt( $ch,CURLOPT_SSL_VERIFYPEER, false );

curl_setopt( $ch,CURLOPT_POSTFIELDS, json_encode( $fields ) );

$result = curl_exec($ch );

curl_close( $ch );

echo $result;

?>

And caught the Data in onMessageReceived

public class MyFirebaseMessagingService extends FirebaseMessagingService     {

  private static final String TAG = "MyFirebaseMsgService";

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
    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());

      sendNotification(remoteMessage.getData().get("message"));
     }
   // Check if message contains a notification payload.
    else if (remoteMessage.getNotification() != null) {
        Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
    sendNotification(remoteMessage.getNotification().getBody());
    }


}
   private void sendNotification(String messageBody) {
    Intent intent = new Intent(this, Notify.class).putExtra("msg",messageBody);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
            PendingIntent.FLAG_ONE_SHOT);

    String channelId = "idddd";
    Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
    NotificationCompat.Builder notificationBuilder =
            new NotificationCompat.Builder(MyFirebaseMessagingService.this)
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setContentTitle("FCM Message")
                    .setContentText(messageBody)
                    .setAutoCancel(true)
                    .setSound(defaultSoundUri)
                    .setContentIntent(pendingIntent);

    NotificationManager notificationManager =
            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

    notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
}
}
Decidua answered 17/12, 2017 at 17:12 Comment(3)
@Tabishkhan Yes brother it is working if you have any issue feel free to ask me.. thanksDecidua
hi @AndroidSanaullah , can you explain the first part the server code, where do you actually put it, I'm facing the same issue but I don't quite understand the server part, are you using postman?Schwenk
curl is used for the request and all parameters are passed to it.@SchwenkDecidua
D
4

In general

There are two types of messages in FCM (Firebase Cloud Messaging):

  • Display Messages: These messages trigger the onMessageReceived() callback only when your app is in foreground

  • Data Messages: Theses messages trigger the onMessageReceived() callback even if your app is in foreground/background/killed

Data Messages example:

{ 
  "to": "/path", 
  "data": 
     { 
      "my_custom_key": "my_custom_value", 
      "my_custom_key2": true 
     } 
}

Display Messages example:

 {
     "notification": {
            "title" : "title",
            "body"  : "body text",
            "icon"  : "ic_notification",
            "click_action" : "OPEN_ACTIVITY_1"
        }
   }

Android side can handle notifications like:

public class MyFirebaseMessagingService extends FirebaseMessagingService {
    …

      @Override public void onMessageReceived(RemoteMessage remoteMessage){
           Map<String, String> data = remoteMessage.getData();
           String myCustomKey = data.get("my_custom_key");
        
       } 

    …

}

More details about FCM you can find here : Set up a Firebase Cloud Messaging client app on Android

Decaffeinate answered 28/9, 2020 at 7:19 Comment(1)
Hi can you please elaborate? I am currently sending a data only payload to my ionic application that is using FCM. It works fine if app is in background/foreground. But doesnt work when application is closed. What is the event that can help me detect when the data payload is received on the app when it is closed?Mediant
K
4

I added the following code in the firebase-messaging-sw.js,

messaging.onBackgroundmessage((payload)=>{
    console.log("background message detected!!");
    console.log("message : ", payload);
})

this was triggered every time message received in background. but I was unable to use the payload in the main thread as SW didn't supported it. so then I researched a lot and found a solution in an Android forum.

so the solution is that we will have to remove the notification payload from the request payload.

so I changed my payload from

{
    "notification": {
        "title": "Hey there",
        "body": "Subscribe to AMAL MOHAN N youtube channel"
    },
    "to": "your-browser-token",
    "data": {
        "value1": "text",
        "value2": "",
        "value3": "sample3",
        "value4": "sample4"
    }
}

to

{
    "to": "your-browser-token",
    "data": {
            "value1": "text",
            "value2": "",
            "value3": "sample3",
            "value4": "sample4"
          }
}

change in the payload automatically made the receiveMessage() trigger in foreground messages as well as background messages.

I found this in an Android forum and this worked for me! please let me know if this works for you.

Kweilin answered 13/7, 2021 at 12:16 Comment(1)
Hi can you please elaborate? I am currently sending a data only payload to my ionic application that is using FCM. It works fine if app is in background/foreground. But doesnt work when application is closed. What is the event that can help me detect when the data payload is received on the app when it is closed?Mediant
S
4

To be able to retrieve data from firebase notification sent when app is in background, you need to add click_action entry in your notification data set.

Set additional options of your notification in firebase console like this: (you have to include any extra data that you want to retrieve in your app here): add click_action to firebase notification

And include the intent filter in your manifest file under the activity to be launched

    <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.MyApp.SplashScreen">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            <intent-filter>
                <action android:name="FIREBASE_NOTIFICATION_CLICK" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
    </activity>

Then get the bundle data in your activity onNewIntent:

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        Bundle data = intent.getExtras();
        if (data != null) {
            for (String key : data.keySet()) {
                Object value = data.get(key);
                // do what you want with the data entries
                Log.d(FIREBASE_TAG, "Key: " + key + " Value: " + value);
                Toast.makeText(this, "Key: "+key+"....  Value: "+value, Toast.LENGTH_LONG).show;
            }
        }
    }

When your app is in foreground, you can set your onMessageReceived like this:

@Override
    public void onMessageReceived(@NonNull RemoteMessage message) {
        Log.d(FIREBASE_TAG, "Message From: " + message.getFrom());

        if (message.getNotification() != null) {
            Intent intent = new Intent(this, MainActivity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            Map<String, String> data = message.getData();
            if(data != null && !data.isEmpty()){
                for(Map.Entry<String ,String > entry : data.entrySet()) {
                    intent.putExtra(entry.getKey(), entry.getValue());
                }
            }
            //.......
            // implement the rest of the code to show notification
            //
        }
    }
Stichous answered 14/9, 2022 at 23:6 Comment(0)
S
3

The easy way to send messages even if the app is in background and foreground as follow:- To send a message using API, you can use a tool called AdvancedREST Client, its a chrome extension, and send a message with the following parameters.

Rest client tool Link: https://chrome.google.com/webstore/detail/advanced-rest-client/hgmloofddffdnphfgcellkdfbfbjeloo

use this url:- https://fcm.googleapis.com/fcm/send Content-Type:application/json Authorization:key=Your Server key From or Authoization key(see below ref)

{ "data": {
    "image": "https://static.pexels.com/photos/4825/red-love-romantic-flowers.jpg",
    "message": "Firebase Push Message Using API"
    "AnotherActivity": "True"
     },
  "to" : "device id Or Device token"
}

Authorization key can be obtained by visiting Google developers console and click on Credentials button on the left menu for your project. Among the API keys listed, the server key will be your authorization key.

And you need to put tokenID of the receiver in the “to” section of your POST request sent using API.

Sedation answered 24/11, 2016 at 12:50 Comment(0)
C
3

you want to work onMessageReceived(RemoteMessage remoteMessage) in background send only data part notification part this:

"data":    "image": "",    "message": "Firebase Push Message Using API", 

"AnotherActivity": "True", "to" : "device id Or Device token"

By this onMessageRecivied is call background and foreground no need to handle notification using notification tray on your launcher activity. Handle data payload in using this:

  public void onMessageReceived(RemoteMessage remoteMessage)
    if (remoteMessage.getData().size() > 0) 
    Log.d(TAG, "Message data payload: " + remoteMessage.getData());      
Clementclementas answered 12/10, 2018 at 6:37 Comment(0)
G
3

The solutions provided didn't work in my case.

At the end of the day, I figured out that some battery optimization apps give users the control to allow or block apps from sending notifications. In my case it was Samsung's Smart Manager that was automatically blocking my app as soon as it was killed/cleaned from the recents list.

Turning off that feature for my app was the only solution I could find.

Gyrocompass answered 24/6, 2021 at 6:43 Comment(0)
P
2

June 2018 Answer -

You have to make sure there is not a "notification" keyword anywhere in the message. Only include "data", and the app will be able to handle the message in onMessageReceived, even if in background or killed.

Using Cloud Functions:

const message = {
    token: token_id,   // obtain device token id by querying data in firebase
    data: {
       title: "my_custom_title",
       body:  "my_custom_body_message"
       }
    }


return admin.messaging().send(message).then(response => {
    // handle response
});

Then in your onMessageReceived(), in your class extending com.google.firebase.messaging.FirebaseMessagingService :

if (data != null) {
  Log.d(TAG, "data title is: " + data.get("title");
  Log.d(TAG, "data body is: " + data.get("body");
}

// build notification using the body, title, and whatever else you want.
Prakrit answered 26/6, 2018 at 3:29 Comment(3)
do you have any source, is it safe?Discriminating
It's safe, I use it in my apps. However, 6 months since posting, I can't remember the source - I imagine it's the firebase documentation.Prakrit
Hi can you please elaborate? I am currently sending a data only payload to my ionic application that is using FCM. It works fine if app is in background/foreground. But doesnt work when application is closed. What is the event that can help me detect when the data payload is received on the app when it is closed?Mediant
M
2

According to OAUTH 2.0:

There will be Auth problem for this case beacuse FCM now using OAUTH 2

So I read firebase documentation and according to documentation new way to post data message is;

POST: https://fcm.googleapis.com/v1/projects/YOUR_FIREBASEDB_ID/messages:send

Headers

Key: Content-Type, Value: application/json

Auth

Bearer YOUR_TOKEN 

Example Body

{
   "message":{
    "topic" : "xxx",
    "data" : {
         "body" : "This is a Firebase Cloud Messaging Topic Message!",
         "title" : "FCM Message"
          }
      }
 }

In the url there is Database Id which you can find it on your firebase console. (Go project setttings)

And now lets take our token (It will valid only 1 hr):

First in the Firebase console, open Settings > Service Accounts. Click Generate New Private Key, securely store the JSON file containing the key. I was need this JSON file to authorize server requests manually. I downloaded it.

Then I create a node.js project and used this function to get my token;

var PROJECT_ID = 'YOUR_PROJECT_ID';
var HOST = 'fcm.googleapis.com';
var PATH = '/v1/projects/' + PROJECT_ID + '/messages:send';
var MESSAGING_SCOPE = 'https://www.googleapis.com/auth/firebase.messaging';
var SCOPES = [MESSAGING_SCOPE];

  router.get('/', function(req, res, next) {
      res.render('index', { title: 'Express' });
      getAccessToken().then(function(accessToken) {
        console.log("TOKEN: "+accessToken)
      })

    });

function getAccessToken() {
return new Promise(function(resolve, reject) {
    var key = require('./YOUR_DOWNLOADED_JSON_FILE.json');
    var jwtClient = new google.auth.JWT(
        key.client_email,
        null,
        key.private_key,
        SCOPES,
        null
    );
    jwtClient.authorize(function(err, tokens) {
        if (err) {
            reject(err);
            return;
        }
        resolve(tokens.access_token);
    });
});
}

Now I can use this token in my post request. Then I post my data message, and it is now handled by my apps onMessageReceived function.

Mauriciomaurie answered 18/2, 2019 at 7:46 Comment(1)
Accepted answer is working, barer token auth is not the way, you need read this to try it with Postman: #45310174Arron
D
2

Since 2019, Google Firebase has a big change in their APIs I mean: 'com.google.firebase:firebase-messaging:18.0.0'

in 18.0.0 they removed MyFirebaseInstanceIDService and you need to get token in MyFirebaseMessagingService so you just need to write :

@Override
public void onNewToken(String token) {
    Log.d(TAG, "Refreshed token: " + token);

}

and also in your AndroidManifest.xml, you have to remove :

<service android:name=".service.MyFirebaseInstanceIDService">
        <intent-filter>
            <action android:name="com.google.firebase.INSTANCE_ID_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:

    <meta-data
        android:name="com.google.firebase.messaging.default_notification_icon"
        android:resource="@drawable/ic_notification" />

    <meta-data
        android:name="com.google.firebase.messaging.default_notification_color"
        android:resource="@color/colorAccent" />

    <meta-data
        android:name="com.google.firebase.messaging.default_notification_channel_id"
        android:value="@string/push_channel" />

now to handle notification messages in a backgrounded app you should define an Intent in your first Activity even if it is SplashScreen, 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.

for example, if your Json is like this:

 "data": {
"message": "2",
"title": "1",
"pushType" : "banner",
"bannerLink": "http://www.google.com",
"image" : "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png"}

you just need to write a simple intent to get those values:

        Bundle extras = intent.getExtras();
        String bannerLink = extras.getString("bannerLink");
        ...
        String channelId = extras.getString("channelId");
Dace answered 25/5, 2019 at 14:22 Comment(0)
O
0

In addition to above answers, If you are testing push notifications using FCM console, 'data' key and object is not added to Push Notification bundle. So you will not receive detailed push notification when App is background or killed.

In this case you have to opt for your back end admin console to test App background scenario.

Here, you will have added 'data' key to your push bundle. so, detailed push will be shown as expected. Hope this helps few.

Ocd answered 27/8, 2018 at 17:29 Comment(1)
Hi can you please elaborate? I am currently sending a data only payload to my ionic application that is using FCM. It works fine if app is in background/foreground. But doesnt work when application is closedMediant
R
0

Using this code you can get the notification in background/foreground and also put action:

//Data should come in this format from the notification
{
  "to": "/xyz/Notifications",
  "data": {
      "key1": "title notification",
      "key2": "description notification"
  }
}

In-App use this code:

  @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);
      String key1Data = remoteMessage.getData().get("key1");
      // use key1Data to according to your need
    }
Radioelement answered 20/9, 2018 at 5:57 Comment(2)
//Data should come in this format from the notification { "to": "/xyz/Notifications", "data": { "key1": "title notification", "key2": "description notification" } } How to write this code in php service?Creon
Hi can you please elaborate? I am currently sending a data only payload to my ionic application that is using FCM. It works fine if app is in background/foreground. But doesnt work when application is closed. What is the event that can help me detect when the data payload is received on the app when it is closed?Mediant
K
0

I solved this issue by using Broadcast Message.

create an Broadcast and send the payload in broadcast Message from the service worker.

then Receive the payload inside application and handle it the way you want.

Kweilin answered 22/9, 2021 at 3:31 Comment(0)
H
0

This case is only for firebase admin notification If the application is in the background FirebaseMessagingService does not get triggered

For handling this case go to launcher activity and check for the intent bundle which is attached, and print out all data using this code :

 intent?.extras?.let {bundle->
        for (key in bundle.keySet()) {
            val value = bundle[key]
            Log.d("NotificationData", String.format("%s %s (%s)", key, value.toString(), value!!.javaClass.name))
        }
    }

check-in log all data with data types is displayed ;

Example :

I want to get the Week and title from notifications will use this code

intent?.extras?.let {it->
        if (it.containsKey("week")){
            
        }
        if (it.containsKey("title")){

        }
Harmattan answered 6/1, 2023 at 12:9 Comment(0)
M
0

January 2023 For those implementing the latest Firebase Cloud Message (FCM), you may not be restricted to sending data and notifications separately for your app to process data while it is in the background or completely off. As has been explained by some answers here, the short version is:

On your launcher activity, monitor the extras on starts; Test if your unique key from your FCM data is in in the list; If present, grab the necessary data and call your activity to handle the processing you wanted to do.

//Firebase
// [START handle_data_extras]
if (getIntent().getExtras() != null) {
    boolean fcmExtraFlag = false;
    for (String key : getIntent().getExtras().keySet()) {
        Object value = getIntent().getExtras().get(key);
        Log.d(TAG, "Key: " + key + " Value: " + value);
        if(key.equalsIgnoreCase("tracerId")){
            //test your known key to be sure it is from fcm
            //this must have come from notification (system) tray
            //this will come whether the app was in the background or completely off
            //generally, this could be in the main activity as it has the intent-filter already set
            fcmExtraFlag = true;
        }
    }
    //pick fcm values if present and notify and/or process accordingly
    //you may add time-lookup to ignore delayed (time-passed) ones; and ignore
    if(fcmExtraFlag){
        String tracerId = (String) getIntent().getExtras().get("tracerId"); 
        //prepare your data as needed
        String tracerData = tracerId+">"+data-one+">"+data-two;
        String msgBody = "This is a test notification; data received: "+tracerId;
        String fcmMessage = msgBody;
        //start your confirmation activity, directly or whichever way
        SidUtils.firebaseStartConfirms(msgBody, tracerData, this);
    }
}
// [END handle_data_extras]

As already said, if possible, this should be in your main-activity so as to handle cases where your app was actually off - not just in the background. These will be triggered by clicks on your app-notifications on the system tray.

Most answered 12/1, 2023 at 12:54 Comment(0)
O
-6

I've experienced the same problem and recompiled the firebase library and prevented it from sending notifications when the application is in the background

*library https://github.com/erdalceylan/com-google-firebase-messaging

 dependencies {
        compile 'com.google.firebase:firebase-core:11.2.0'
        compile 'com.github.erdalceylan:com-google-firebase-messaging:v1-11.2.0'
    }

*

@WorkerThread
public void onMessageReceived(RemoteMessage var1) {
  //your app is in background or foreground all time calling
}

hope helps. Good luck

Omentum answered 26/9, 2017 at 7:29 Comment(2)
Not usefull answerDowable
This is a horrible suggestion.Couching

© 2022 - 2024 — McMap. All rights reserved.