应用程序在 Firebase 后台运行时如何处理通知

How to handle notification when app in background in Firebase

这是我的清单:

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

当应用程序在后台并且收到通知时,默认通知会收到,但不会收到 运行 我的代码 onMessageReceived

这是我的 onMessageReceived 代码。如果我的应用程序在前台 运行ning,而不是在后台 运行ning 时,就会调用此方法。当应用程序也在后台时,我如何 运行 此代码?

// [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]

根据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>

编辑:

基于此

您无法使用 Firebase 控制台设置 click_action 负载。您可以尝试使用 curl 命令或自定义 http 服务器进行测试

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\"}}"

1。为什么会这样?

FCM (Firebase Cloud Messaging) 中有两种类型的消息:

  1. 显示消息:只有当您的应用处于前台
  2. 时,这些消息才会触发onMessageReceived()回调
  3. 数据消息:这些消息会触发 onMessageReceived() 回调 even 如果您的应用在 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。怎么做?

为此,您必须向以下 URL:

执行 POST 请求

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

Headers

  • 键: Content-Type值: application/json
  • 键: Authorization值: key=<your-server-key>

Body 使用主题

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

或者如果您想将其发送到特定设备

{
    "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。如何处理推送消息?

这是您处理收到的消息的方式:

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

     // Manage data
}

让 firebase 库在以下情况下调用您的 onMessageReceived()

  1. 应用程序在前台
  2. 应用程序在后台
  3. 应用已被杀死

您不得将 JSON 密钥 notification 放入您对 Firebase API 的请求中,而是使用 data,见下文。

当您的应用程序处于后台或被杀死时,以下消息不会调用您的 onMessageReceived(),并且您无法自定义通知。

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

但是使用这个会起作用

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

基本上,消息在参数 RemoteMessage 中与您的数据对象一起作为 Map<String, String> 发送,然后您可以在 onMessageReceived 中管理通知,就像此处的代码片段一样

@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
    */
}

根据 send downstream using firebase 中的 firebase 文档,有 2 种有效负载:

  1. 数据

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

    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.

当您在前台时,您可以使用 onMessageReceived() 获取 FCM 内的数据,您可以从 data 获取数据有效负载。

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

当您处于后台时,FCM 将根据来自 notification 负载的信息在系统托盘中显示通知。用于系统托盘上通知的标题、消息和图标是从 notification 有效负载中获取的。

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

这个notification有效载荷用于当你的应用程序在后台时自动在系统托盘上显示通知。 要在您的应用程序在后台时获取通知数据,您应该在 notification 有效负载中添加 click_action。

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>

将 intent-filter 放在您的清单上,在您的 activity 标签之一内。当您点击通知时,它会打开应用程序并直接转到您在 click_action 中定义的 activity,在本例中为 "OPEN_ACTIVTY_1"。 在 activity 中,您可以通过以下方式获取数据:

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

我正在为我的 android 应用程序使用 FCM 并使用两个有效负载。 这是我正在使用的示例 JSON :

{
  "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"
   }
}

这里是关于 firebase 消息的更清晰的概念。我是从他们的支持团队那里找到的。

Firebase 具有三种消息类型:

通知消息 : 通知消息在后台或前台工作。当应用程序处于后台时,通知消息会传送到系统托盘。如果应用程序在前台,则消息由 onMessageReceived()didReceiveRemoteNotification 回调处理。这些本质上就是所谓的显示消息。

数据消息:在Android平台上,数据消息可以在后台和前台工作。数据消息将由 onMessageReceived() 处理。此处的平台特定说明是:在 Android 上,可以在用于启动 activity 的 Intent 中检索数据有效负载。详细来说,如果您有 "click_action":"launch_Activity_1",您可以通过 getIntent() 仅从 Activity_1.

检索此意图

具有通知和数据负载的消息:在后台时,应用程序在通知托盘中接收通知负载,并且仅在用户点击时处理数据负载通知。在前台时,您的应用程序会收到一个消息对象,其中包含两个有效负载。其次,click_action 参数通常用于通知有效载荷而不是数据有效载荷。如果在数据负载中使用,此参数将被视为自定义键值对,因此您需要实现自定义逻辑才能使其按预期工作。

此外,我建议您使用 onMessageReceived 方法(请参阅数据消息)来提取数据包。根据你的逻辑,我检查了 bundle 对象并没有找到预期的数据内容。这是对类似案例的参考,可能会更清楚。

有关详细信息,请访问我的

要在后台捕获消息,您需要使用 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)
        }
    }

并将其添加到您的清单中:

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

从您的服务器请求中完全删除通知负载仅发送数据并在onMessageReceived()中处理它,否则当应用程序处于后台或被杀死时,您的onMessageReceived将不会被触发。

这是我从服务器发送的内容:

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

因此您可以像这样在 onMessageReceived(RemoteMessage message) 中接收数据:(假设我必须获取 ID)

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

同样,您可以在 onMessageReceived().

内获取从服务器发送的任何数据

像这样简单的总结

  • 如果您的应用是 运行;

    onMessageReceived()
    

是触发器。

  • 如果您的应用不是 运行(被滑动杀死);

    onMessageReceived()
    

不是直接触发和传递的。如果您有任何特殊的键值对。它们不工作是因为 onMessageReceived() 不工作。

我找到了这个方法;

在您的启动器 activity 中,放入此逻辑,

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

在此 if 块中,根据 firebase UI 搜索您的密钥。

在此示例中,我的键和值如上; (对不起语言=))

当我的代码运行时,我得到 "com.rda.note"。

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

通过这行代码,我关闭了我的应用程序并打开了 Google Play Market

快乐编码=)

即使应用程序在后台和前台也可以发送消息的简单方法如下:- 要使用 API 发送消息,您可以使用名为 AdvancedREST Client 的工具,它是 chrome 扩展,并使用以下参数发送消息。

休息客户端工具Link:https://chrome.google.com/webstore/detail/advanced-rest-client/hgmloofddffdnphfgcellkdfbfbjeloo

使用这个 url:- https://fcm.googleapis.com/fcm/send 内容类型:application/json Authorization:key=您的服务器密钥来自或授权密钥(参见下面的参考)

{ "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"
}

可以通过访问 Google 开发人员控制台并单击项目左侧菜单中的凭据按钮来获取授权密钥。在列出的 API 个密钥中,服务器密钥将是您的授权密钥。

并且您需要将接收方的 tokenID 放入使用 API 发送的 POST 请求的“收件人”部分。

因为从 Firebase 通知 UI 发送的 display-messages 仅在您的应用程序处于前台时才有效。对于 data-messages,需要对 FCM

进行 POST 调用

步骤

  1. 安装 Advanced Rest Client Google Chrome 扩展

  2. 添加以下内容headers

    :Content-Type,:application/json

    Key:授权,Value:key="your server key"

  3. 添加body

    • 如果使用主题:

      {
          "to" : "/topics/topic_name",
          "data": {
          "key1" : "value1",
          "key2" : "value2",
          }
      }
      
    • 如果使用注册id:

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

就是这样!现在照常收听 onMessageReceived 回调。

@Override
public void onMessageReceived(RemoteMessage remoteMessage) { 
     Map<String, String> data = remoteMessage.getData();
     String value1 = data.get("key1");
     String value2 = data.get("key2");
}

我觉得所有回复都不完整,但所有回复都包含您需要的内容,以便您在应用程序处于后台时处理包含数据的通知。

按照这些步骤,您将能够在您的应用程序处于后台时处理您的通知。

  1. 像这样添加一个intent-filter

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

发送给您要处理通知数据的activity。

  1. 使用以下格式发送通知:

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

这里的关键是添加

"click_action" : ".MainActivity"

其中 .MainActivity 是 activity 和您在步骤 1 中添加的 intent-filter

  1. .MainActivity 的 onCreate 通知中获取 data 信息:

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

这就是您需要做的全部。我希望这对某人有所帮助:)

根据文档:2017 年 5 月 17 日

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.

因此,您应该同时使用负载通知和数据:

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

没有必要使用 click_action.You 应该只是从 LAUNCHER activity

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

Java 代码应该在 MainActivity 的 onCreate 方法上:

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

您可以测试来自 Firebase Notifications Console 的负载通知和数据。不要忘记在高级选项部分填写自定义数据字段

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {

}

不是每次都调用只在app在前台时调用

有一个覆盖方法每次都会调用此方法,无论什么应用程序在前台或后台或被杀死但此方法适用于此 firebase api 版本

这是您必须从 gradle

导入的版本
compile 'com.google.firebase:firebase-messaging:10.2.1'

这就是方法

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

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


}

对于之前的 firebase api 这种方法不存在,所以在那种情况下 当应用程序在后台时,fire base 自行处理......现在你有了这个方法 你想做什么......你可以在这里用这种方法做.....

如果您使用的是以前的版本,默认 activity 将启动 在那种情况下,你可以用同样的方式获取数据

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

// 随心所欲.... }

通常这是我们在通知中收到的来自服务器的结构

{
    "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": "......."
    }
}

这取决于你想如何提供该数据密钥,或者你想通知你可以提供的任何东西....... 您将使用相同的密钥在这里提供什么,您将获得该数据.........

在少数情况下,如果您不发送点击操作,在这种情况下,当您点击通知时,默认 activity 将打开,但如果您想要在应用程序打开时打开您的特定 activity背景 你可以在 handleIntent 方法上从这个调用你的 activity 因为每次

都会调用它

2017 年更新答案

这里是 docs 对此的明确回答:

我想出了场景,

当应用处于前台时, onMessageReceived() 方法是从 FirebaseService 调用的。所以 pendingIntent 在服务中定义 class 将被调用。

当应用处于后台时,首先调用activity

现在,如果您使用 标语 activity,那么 必须 记住 标语activity 将被调用,否则如果没有 splashActivity,则无论第一个 activity 是什么,都将被调用。

然后你需要检查 firstActivitygetIntent() 看看它是否有任何 bundle 。如果一切正常,你会看到 bundle 已经填充了值。如果从服务器发送的 数据标签 中的值看起来像这样,

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

然后在第一个activity中,你会看到, 有一个有效的意图(getIntent() is not null),有效的 bundle 和内部 bundle ,上面提到的整个 JSON 和 data 作为 key.

对于这种情况,提取值的代码如下所示,

    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();
                }


            }
        }

我遇到了同样的问题,重新编译了 firebase 库,阻止它在应用程序处于后台时发送通知

*图书馆 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
}

希望有所帮助。祝你好运

感谢大家的回答。但我通过发送 数据消息 而不是发送 Notification 解决了这个问题。 服务器代码

<?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;

?>

并在 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());
}
}

2018 年 6 月答案 -

您必须确保邮件中的任何地方都没有 "notification" 关键字。仅包含 "data",应用程序将能够处理 onMessageReceived 中的消息,即使在后台或被杀死。

使用云函数:

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
});

然后在你的 onMessageReceived() 中,在你的 class 中扩展 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.

除了以上答案, 如果您正在使用 FCM 控制台 测试推送通知,'data' 键和对象 添加到推送通知包。所以当应用程序后台或被杀死时,您将不会收到详细的推送通知。

在这种情况下,您必须选择后端管理控制台来测试应用程序后台场景。

在这里,您已将 'data' 密钥添加到您的推送包中。因此,将按预期显示详细推送。 希望这对少数人有所帮助。

使用此代码,您可以获得 background/foreground 中的通知,并采取行动:

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

应用内使用此代码:

  @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);
      String key1Data = remoteMessage.getData().get("key1");
      // use key1Data to according to your need
    }

您想在后台处理 MessageReceived(RemoteMessage remoteMessage) 仅发送数据部分通知部分:

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

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

通过这个 onMessageRecivied 调用后台和前台,无需使用启动器上的通知托盘处理通知 activity。 使用此处理数据负载:

  public void onMessageReceived(RemoteMessage remoteMessage)
    if (remoteMessage.getData().size() > 0) 
    Log.d(TAG, "Message data payload: " + remoteMessage.getData());      

根据 OAUTH 2.0:

由于 FCM 现在使用 OAUTH 2

,这种情况下会有 Auth 问题

所以我阅读了 firebase 文档,根据文档,post 数据消息的新方法是;

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

Headers

Key: Content-Type, Value: application/json

授权

Bearer YOUR_TOKEN 

例子Body

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

在 url 中有数据库 ID,您可以在您的 firebase 控制台上找到它。 (转到项目设置)

现在让我们拿走我们的令牌(有效期只有 1 小时):

首先在 Firebase 控制台中,打开设置 > 服务帐户。单击 生成新私钥 ,安全地存储包含密钥的 JSON 文件。我需要这个 JSON 文件来手动授权服务器请求。我下载了。

然后我创建一个node.js项目并使用这个函数来获取我的令牌;

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);
    });
});
}

现在我可以在我的 post 请求中使用这个令牌。然后我 post 我的数据消息,它现在由我的应用程序 onMessageReceived 函数处理。

自 2019 年以来,Google Firebase 的 API 发生了很大变化 我是说: 'com.google.firebase:firebase-messaging:18.0.0'

在 18.0.0 中他们删除了 MyFirebaseInstanceIDService 并且你需要在 MyFirebaseMessagingService 中获取令牌所以你只需要写:

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

}

并且在您的 AndroidManifest.xml 中,您必须删除 :

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

此外,建议您设置默认值以自定义通知的外观。您可以指定自定义默认图标和自定义默认颜色,只要通知负载中未设置等效值,就会应用这些图标和自定义默认颜色。

在应用程序标签内添加这些行以设置自定义默认图标和自定义颜色:

    <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" />

现在要在后台应用程序中处理通知消息,您应该在第一个 Activity 中定义一个 Intent,即使它是 SplashScreen,当您的应用程序在后台时,Android 会将通知消息定向到系统托盘。用户点击通知会默认打开应用程序启动器。

例如,如果您的 Json 是这样的:

 "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"}

您只需要编写一个简单的意图来获取这些值:

        Bundle extras = intent.getExtras();
        String bannerLink = extras.getString("bannerLink");
        ...
        String channelId = extras.getString("channelId");

截至 2019 年 7 月工作

Android compileSdkVersion 28、buildToolsVersion 28.0.3 和 firebase-messaging:19.0.1

在研究了所有其他 Whosebug 问题和答案并尝试了无数过时的解决方案之后,该解决方案设法在这 3 种情况下显示通知:

- 应用程序在前台:
我的 MyFirebaseMessagingService class

的 onMessageReceived 方法收到通知

- 应用已被杀死(后台不是运行): 通知由 FCM 自动发送到通知托盘。当用户触摸通知时,通过调用清单中具有 android.intent.category.LAUNCHER 的 activity 来启动应用程序。您可以在 onCreate() 方法中使用 getIntent().getExtras() 来获取通知的数据部分。

- 应用程序在后台: 通知由 FCM 自动发送到通知托盘。当用户触摸通知时,通过启动清单中具有 android.intent.category.LAUNCHER 的 activity 将应用带到前台。由于我的应用程序在 activity 中具有 launchMode="singleTop",因此不会调用 onCreate() 方法,因为已经创建了相同 class 的一个 activity,而不是 onNewIntent( ) 调用 class 的方法,您可以使用 intent.getExtras().

获取通知的数据部分

步骤: 1- 如果您像这样定义应用程序的主要 activity:

<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- 在 MainActivity.class

的 onCreate() 方法中添加这些行
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);
    }
}

和这些方法一样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- 像这样创建 class MyFirebase:

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- 创建一个新的 class NotificationActivity.class 像这样:

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- 将这些行添加到您的应用程序清单中,在您的标签内

    <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- 在您的 Application.java onCreate() 方法或 MainActivity.class onCreate() 方法中添加这些行:

      // 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));
  }

完成。

现在要使其在上述 3 种情况下正常运行,您必须按以下方式从 Firebase 网络控制台发送通知:

在通知部分: Notification Title = 在通知对话框中显示的标题(可选) 通知文本 = 向用户显示的消息(必需) 然后在目标部分: App = 您的 Android 应用 在其他选项部分: Android 通知渠道 = default_channel_id 自定义数据 键:标题值:(此处的文本与通知部分的标题字段中的文本相同) 键:body 值:(此处的文本与通知部分的消息字段中的文本相同) key:click_action 值:.MainActivity 声音=禁用
到期=4 周

您可以在模拟器中使用 API 28 和 Google 播放进行调试。

编码愉快!

我遇到了同样的问题。在深入研究了为什么我的 MainActivity 是有意调用而没有数据之后,我意识到我的 LAUNCHER activity(如清单中所示)是 SplashActivity。在那里我找到了消息数据并将它们转发给 MainActivity。像沙姆一样工作。我相信这可以帮助某人。

感谢所有其他答案。

一般

FCM (Firebase Cloud Messaging) 中有两种类型的消息:

  • 显示消息: 只有当您的应用处于 前台[= 时,这些消息才会触发 onMessageReceived() 回调14=]

  • 数据消息: 即使您的应用处于 foreground/background/killed[=40,这些消息也会触发 onMessageReceived() 回调=]

数据消息 示例:

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

显示消息 示例:

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

Android 端可以处理如下通知:

public class MyFirebaseMessagingService extends FirebaseMessagingService {
    …

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

    …

}

您可以在此处找到有关 FCM 的更多详细信息:Set up a Firebase Cloud Messaging client app on Android

所提供的解决方案对我的情况无效。

最后,我发现一些电池优化应用程序允许用户控制允许或阻止应用程序发送通知。就我而言,是三星的智能管理器在我的应用程序从最近列表中 killed/cleaned 时自动阻止它。

为我的应用关闭该功能是我能找到的唯一解决方案。

我在firebase-messaging-sw.js,

中添加了如下代码
messaging.onBackgroundmessage((payload)=>{
    console.log("background message detected!!");
    console.log("message : ", payload);
})

每次在后台收到消息时都会触发此事件。但我无法在主线程中使用有效负载,因为 SW 不支持它。所以我研究了很多并在 Android 论坛中找到了解决方案。

所以解决方案是我们必须从请求负载中删除通知负载。

所以我从

更改了我的负载
{
    "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": "your-browser-token",
    "data": {
            "value1": "text",
            "value2": "",
            "value3": "sample3",
            "value4": "sample4"
          }
}

负载的变化自动使 receiveMessage() 在前台消息和后台消息中触发。

我在 Android 论坛上找到了这个,它对我有用!如果这对你有用,请告诉我。

我使用广播消息解决了这个问题。

创建一个广播并从服务工作者发送广播消息中的负载。

然后在应用程序中接收负载并按照您想要的方式处理它。