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


426

这是我的清单

    <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代码。如果我的应用程序在前台运行,而不是在后台运行,则调用此方法。当应用程序也在后台运行时,如何运行此代码?

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

1
它写在onMessageReceived()替代示例中,第二条注释行说Not getting messages here? See why this may be: goo.gl/39bRNJ 。与以下答案一样,该解决方案可在带有通知和数据有效负载的消息
fllo

简而言之,要唤醒已终止的应用程序,您应始终发送带有数据对象的通知,以在应用程序中调用通知服务类处理程序FirebaseMessagingService.onMessageReceived()。也可以尝试不从Firebase控制台发送它,而是从其他地方发送它(例如,在线测试后服务)。
Zon

1
这个解决方案为我工作stackoverflow.com/a/44150822/6632278希望能对您有所帮助。祝您好运
Tony Barajas

什么“ .fcm”。PshycoFirebaseMessagingServices是否在您的清单中?我收到找不到类的错误..并且在任何地方都找不到该参数的第一部分是什么。
–ÉderRocha Bezerra

Answers:


660

1.为什么会这样?

FCM(Firebase云消息传递)中有两种消息类型:

  1. 显示消息onMessageReceived()仅当您的应用程序处于前台时,这些消息才触发回调
  2. 数据消息即使您的应用程序处于前台/后台/已终止状态,这些消息也会触发onMessageReceived()回调

注意: Firebase团队尚未开发可发送data-messages到您的设备的UI 。您应该使用服务器发送此类型!



2.如何?

为此,您必须对POST以下网址执行请求:

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

标头

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

正文使用主题

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


注意:确保没有添加 JSON密钥。notification
注意:要获取服务器密钥,可以在firebase控制台中找到它: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
}

4
您可以按照以下步骤在相同的通知中发送“数据”和“通知”键stackoverflow.com/a/42279260/2734021 :)
Daniel S.

15
Firebase team have not developed a UI to send data-messages to your devices, yet.过去一年有变化吗?
Hankrecords

2
@Antonio在Oreo中,当应用程序被杀死时,不会调用onMessageReceived。我只是有数据的有效载荷。你有什么更新吗?
萨米尔·曼格罗里亚

63
当应用程序在后台运行时onMessageReceived不会被调用,这在FCM中是一个严重的问题!!!另外,请更新您的答案。
穆罕默德·巴巴尔

2
好吧,对我来说,这似乎发生在某些android设备上。花了几个小时认为这是一个实际的问题,但是后来证明什么都没有。所以我的建议是在不同的设备上进行测试。我什至在虚拟机上对其进行了测试,即使该应用被终止,我也收到了通知。我只是说这是为了节省别人的时间。
Tarek-Dev '18

158

在以下情况下,使Firebase库调用onMessageReceived()

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

您不得在对Firebase API的请求中放入JSON密钥“ notification”,而应使用“ 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一起发送的,然后您可以在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
    */
}

1
是否可以从Firebase控制台实现此目的?
koder

@koder,很遗憾,Firebase控制台不支持此功能,您必须使用工具将发布消息请求发送给firebase,例如curl或postman(chrome插件),firebase消息传递api,请参阅此处的文档-firebase.google.com/docs / cloud-messaging / http-server-ref
Teerakiat Chitawattanarat

1
这是我一天多以来一直在寻找的东西.....非常感谢,它运行得非常好。如果您解释如何在onMessageReceived()中处理数据中的密钥对,以启动具有这些值的活动,那就更好了。
Boopathi T

25
当应用程序在后台运行时,您的方法工作正常,但是当应用程序被杀死时,我没有收到数据
Sanzhar 2016年

8
Sanzhar的同样问题。如果该应用程序被杀死,我不会收到任何消息。
Giacomo M'17年

104

我觉得所有回复都不完整,但是当您的应用程序处于后台时,它们都需要处理一些包含数据的通知。

请按照以下步骤操作,当您的应用程序处于后台时,您将能够处理通知。

1.添加一个意图过滤器,如下所示:

<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是在步骤1中添加的具有意图过滤器的活动。

  1. 从“ .MainActivity”的onCreate中的通知中获取“数据”信息:

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

那应该就是您所要做的。我希望这对某人有帮助:)


5
这应该是正确的答案。没有任何文档说要要求click_action和Intent Filter才能使通知显示在托盘中。它们都是必需的。
airowe '17

@airowe不需要显示通知即可
AlwaysConfused

我既有数据块又有通知块,但是我不能在活动中接收数据?
Ashwani

3
我只是不明白这不是公认的答案。没有click_action,它将无法正常工作。拯救了我的一天
阿伦·尚卡

4
感谢您的支持@ArunShankar。接受的答案在我的回答前七个月就得到了答复,这是一个很好的答案。我不明白为什么没有人谈论click_action,这就是为什么我添加了答案。我很高兴它对很多人都非常有用
Daniel

42

根据使用firebase向下游发送中的firebase文档,有两种有效负载:

  1. 数据

    此参数指定消息有效负载的自定义键值对。客户端应用负责处理数据消息。数据消息仅具有自定义键值对。

  2. 通知

    此参数指定通知有效负载的预定义的,用户可见的键值对。FCM代表客户端应用程序自动将消息显示给最终用户设备。通知消息具有一组预定义的用户可见键。

当您处于前台时,可以使用onMessageReceived()获取FCM内部的数据,您可以从数据有效负载中获取数据

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

当您处于后台时,FCM将基于通知有效内容中的信息在系统任务栏中显示通知。系统托盘上用于通知的标题,消息和图标来自通知有效内容。

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

当您希望在应用程序处于后台时自动在系统任务栏上显示通知时,将使用此通知有效负载。要在后台运行应用程序时获取通知数据,应在通知有效内容内添加click_action 。

如果要打开应用程序并在后台运行时执行特定操作,请在通知有效负载中设置click_action并将其映射到要启动的活动中的意图过滤器。例如,将click_action设置为OPEN_ACTIVITY_1以触发意图过滤器,如下所示:

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

将该意图过滤器放在清单中的活动标签之一内。当您单击通知时,它将打开应用程序并直接进入您在click_action中定义的活动,在本例中为“ OPEN_ACTIVTY_1”。在该活动中,您可以通过以下方式获取数据:

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

我正在将FCM用于我的android应用程序,并同时使用了两个有效负载。这是我正在使用的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"
   }
}

“将意图过滤器放在清单中的应用程序标记内。” 您不能将Intent-filter放入应用程序标记中。
Kyrylo Zapylaiev

您的JSON数据行不显示click_action键的位置。
乔什(Josh)

这不是正确的答案吗?什么是点击动作?有人应该投弃权票或将其清除
rana

1
@KyryloZapylaiev我只是更正并更新了答案:“将意图过滤器放在清单中的活动标记中”。
迪卡

1
@Josh已更新并格式化。您可以再次检查
Dika

32

根据文档

在后台应用程序中处理消息

当您的应用程序在后台运行时,Android会将通知消息定向到系统任务栏。默认情况下,用户点击通知会打开应用启动器。

这包括同时包含通知和数据有效负载的消息。在这些情况下,通知将传递到设备的系统托盘,而数据有效载荷将在启动器活动的意图之外传递。

如果要打开应用程序并执行特定操作,请在通知有效负载中设置click_action并将其映射到要启动的活动中的意图过滤器。例如,将click_action设置为OPEN_ACTIVITY_1以触发意图过滤器,如下所示:

 <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
你是对的。我已经阅读了文档。但是我很困惑将其放在我的清单中?我必须在该java文件的onMessageReceived上运行代码,因此我该怎么办,先生?
Parth Patel

您无法使应用程序在后台运行时自动调用onMessageReveiced。相反,您需要处理收到的意图,并使处理程序对其进行调用。或者,最好是实现由onMessageReveiced和您的意图处理调用的单独的类/方法。我在主要活动的onNewIntent中添加了处理程序,它对我来说很好用。
diidu

为时已晚,无法回复@Parath Patel的问题,但这可能会帮助遇到相同问题的其他人,请在此处查看我的答案stackoverflow.com/a/42279260/2734021
Daniel S.17年

必须在何处执行此操作?我的活动或Firebasemessage服务?
马赫迪

**关于多个通知重定向不起作用?例如:如果我在后台使用“ click_action”从fcm收到三个通知,则第一个通知成功重定向,然后2个通知单击活动重定向不起作用
prasanthMurugan

24

截至2019年7月

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

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

-应用位于前台:
通过MyFirebaseMessagingService类的onMessageReceived方法接收通知

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

-应用程序在后台: FCM自动将通知发送到通知托盘。当用户触摸通知时,通过启动清单中具有android.intent.category.LAUNCHER的活动,将应用程序置于前台。由于我的应用程序在该活动中具有launchMode =“ singleTop”,因此不会调用onCreate()方法,因为已经创建了同一类的一个活动,而是调用了该类的onNewIntent()方法,并且您获得了数据的一部分通过使用intent.getExtras()获得通知。

步骤:1-如果您定义应用的主要活动是这样的:

<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-创建类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-创建一个新的类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 Web控制台发送通知:

在“通知”部分中:通知标题=要在通知对话框中显示的标题(可选)通知文本=要向用户显示的消息(必填)然后在“目标”部分:App =您的Android应用程序,在“其他选项”部分:Android通知频道= default_channel_id自定义数据键:标题值:(此处与“通知”部分的“标题”字段中的文本相同)键:主体值:(此处与“通知”部分的“消息”字段中的文本相同)键:click_action值:.MainActivity声音=失效
期限= 4周

您可以使用带有Google Play的API 28在仿真器中对其进行调试。

编码愉快!


2
感谢您的出色回答。
Alex Chengalan

@alvaro,如果我想在收到通知时打开Url,该如何处理
engmms

应用关闭或被杀死时无法在体内opp手机上工作
Android开发人员

我没有在Vivo手机中尝试过,但目前在其他许多Android手机中都可以使用。请慢慢阅读每一步,检查所有详细信息,然后在我在这里提到的每种方法的第一行打开调试断点,使用电缆将真实电话连接到开发计算机,并在发送时调试应用程序来自FCM的消息。检查您是否正在发送带有我提到的所有参数和格式的FCM消息。祝好运!
alvaro

2
这是最新的答案,因为Firebase文档现在显示为:当您的应用程序在后台运行时,Android会将通知消息定向到系统托盘。默认情况下,用户点击通知会打开应用启动器。这包括同时包含通知和数据有效负载的消息。在这些情况下,通知将传递到设备的系统托盘,而数据有效载荷将在启动器活动的意图之外传递。(firebase.google.com/docs/cloud-messaging/android/…)这么多旧答案已经过时了
Riot Goes Woof

20

由于display-messages从Firebase Notification UI发送的,仅当您的应用程序位于前台时才起作用。对于data-messages,需要对FCM进行POST调用

脚步

  1. 安装Advanced Rest Client Google Chrome扩展程序 在此处输入图片说明

  2. 添加以下标题

    :Content-Type,:application / json

    密钥:授权,:key =“您的服务器密钥” 在此处输入图片说明

  3. 添加身体

    • 如果使用主题:

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

20

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

7
当应用程序处于后台时,它实际上会收到通知消息。但是,它不会阻止默认的Firebase接收器对其进行处理,因此该消息仍将显示为通知警报。
Galya's

目前不起作用,所以这就是我提出此解决方案的原因。Google错误库中存在一个已归档的错误。您可能要检查一下。
Romulano '16

您能否在此处发布指向该错误的链接
Galya,

如何从通知中获取数据?
拉维·瓦格拉

当该应用被终止时,这显然不起作用。我已经尝试了几个小时的解决方案。由于某些原因,接收器在应用程序处于后台运行时工作,而在应用程序被杀死时则无法运行
XcodeNOOB 2016年

16
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {

}

每次仅在应用程序处于前台时才被调用

无论前台是在后台还是后台或被杀死的应用程序,每次都有一个重写方法被调用,但是此方法在此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本身会....现在您拥有该方法,您想要做什么...您可以在此方法中完成。 ...

如果您使用的是先前版本,则默认活动将开始,在这种情况下,您可以以相同方式获取数据

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

由您决定要如何提供该数据密钥,还是要通知您可以提供的任何内容.......您将使用相同的密钥在此处给出的内容,您将得到该数据.....。 。

在少数情况下,如果您在这种情况下不发送点击动作,则当您单击通知时,默认活动将打开,但是如果您想在应用程序处于后台运行时打开您的特定活动,则可以通过handleIntent方法从此调用您的活动每次都被调用


我将firebase消息更新为10.2.1,向通知消息中添加了数据,并且可以正常工作。前景,背景和被杀死。谢谢
Firas Shrourou

在Kotlin中,我得到以下错误:(44,5)'FirebaseMessagingService'中的'handleIntent'是最终的,不能被覆盖
ugali soft

它无法在
Firebase

14

根据文档:2017年5月17日

当您的应用程序处于后台时,Android会将通知消息定向到系统任务栏。用户点击通知会默认打开应用启动器

这包括同时包含通知和数据有效负载的消息(以及从Notifications控制台发送的所有消息)。在这些情况下,通知将传递到设备的系统托盘,而数据有效载荷将在启动器活动的意图之外传递。

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

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

无需使用click_action。您只需从LAUNCHER活动的意图中获得一些信息即可

<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测试两个有效负载通知和数据。不要忘记在“高级选项”部分填写自定义数据字段


13

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

Firebase具有三种消息类型

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

数据消息:在Android平台上,数据消息可以在后台和前台工作。数据消息将由onMessageReceived()处理。这里有一个特定于平台的注释:在Android上,可以在用于启动活动的Intent中检索数据有效负载。要详细说明,如果可以的话"click_action":"launch_Activity_1",可以通过getIntent()only 检索此意图Activity_1

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

另外,我建议您使用onMessageReceived方法(请参阅数据消息)提取数据束。根据您的逻辑,我检查了bundle对象,但未找到预期的数据内容。这里是对类似情况的参考,可能会提供更多的清晰度。

欲了解更多信息,请访问我的这个线程



8

像这样的简单总结

  • 如果您的应用正在运行;

    onMessageReceived()

是触发器。

  • 如果您的应用未运行(通过刷卡杀死);

    onMessageReceived()

不是由直接触发和传递的。如果有特殊的键值对。它们不起作用,因为onMessageReceived()不起作用。

我已经找到了这种方式;

在启动器活动中,输入以下逻辑,

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

快乐编码=)


6

我想出了方案

当应用程序是在前台onMessageReceived()方法从被呼叫FirebaseService。所以的PendingIntent将调用服务类中定义。

当应用程序处于后台时,将调用第一个活动

现在,如果您使用的是飞溅的活动,则必须牢记splashactivity将被调用,否则,如果没有splashActivity,那么无论第一活动,将被调用。

然后,你需要检查getIntent()中的firstActivity,看它是否有任何捆绑。如果一切正常,你会看到包是有与填充值。如果值数据标签从服务器看起来像这样发,

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

然后在第一个活动中,您将看到有一个有效的intent(getIntent()不为null),有效的包和包内,将以数据键的上述整个JSON 。

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

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


            }
        }

3

感谢大家的回答。但是我通过发送数据消息而不是发送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());
}
}

1
它也有背景吗?
塔比汗

@Tabishkhan是的兄弟,如果您有任何问题,可以随时问我。.谢谢
Android Sanaullah

1
嗨,@ AndroidSanaullah,您能解释一下服务器代码的第一部分,您实际上将它放在哪里,我也面临着同样的问题,但是我不太理解服务器部分,您使用的是邮递员吗?
下沉

curl用于请求,所有参数都传递给它。@ Shid
Android Sanaullah,

2

从服务器请求中完全删除通知有效内容发送数据并在中处理数据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()


2

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

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

使用这个网址: - https://fcm.googleapis.com/fcm/send 内容类型:应用程序/ JSON授权:键=您的服务器密钥从或Authoization键(见下文参考)

{ "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请求的“至”部分中。


2

您要在后台处理onMessageReceived(RemoteMessage remoteMessage),仅发送数据部分通知部分,如下所示:

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

“ AnotherActivity”:“ True”,“ to”:“设备ID或设备令牌”

通过此onMessageRecivied调用后台和前台,无需使用启动器活动上的通知托盘处理通知。使用以下方法处理数据有效负载:

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

1

2018年6月答案-

您必须确保消息中的任何地方都没有“ notification”关键字。仅包含“数据”,该应用程序将能够在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()中,在您的类中扩展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.

您有任何资料来源吗?
Yogesh Rathi

很安全,我可以在自己的应用中使用它。但是,自发布以来已有6个月了,我不记得来源了-我想那是firebase文档。
Jeff Padgett,

1

根据OAUTH 2.0:

在这种情况下,由于现在使用OAUTH 2的FCM将出现Auth问题

因此,我阅读了Firebase文档,并根据文档发布数据消息的新方法是:

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

标头

Key: Content-Type, Value: application/json

验证码

Bearer YOUR_TOKEN 

身体示例

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

网址中有数据库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);
    });
});
}

现在,我可以在发布请求中使用此令牌。然后,我发布数据消息,该消息现在由我的应用程序onMessageReceived函数处理。


接受的答案是工作,伯雷尔令牌身份验证是不是这样,你需要阅读这跟邮递员来试试吧:stackoverflow.com/questions/45309674/...
卡洛斯耶稣阿兰西维亚Taborga

1

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

现在要在后台应用程序中处理通知消息,即使在SplashScreen中,您也应该在第一个Activity中定义一个Intent。当您的应用程序在后台时,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");

0

除了上述答案外,如果您正在使用FCM控制台测试推送通知,则“数据”键和对象不会添加到“推送通知”捆绑包中。因此,当应用程序处于后台或被终止运行时,您将不会收到详细的推送通知。

在这种情况下,您必须选择后端管理控制台来测试App后台方案。

在这里,您将在推包中添加“数据”键。因此,详细的推送将按预期显示。希望这对您有所帮助。


0

使用此代码,您可以在后台/前台获取通知,并执行以下操作:

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

//数据应以这种格式来自通知{“ to”:“ / xyz / Notifications”,“ data”:{“ key1”:“标题通知”,“ key2”:“描述通知”}}如何编写这个代码在PHP服务?
塔比汗

-3

会有两种类型的通知

  1. 显示通知-仅显示通知,将仅在未打开应用及其在应用堆栈中显示。
  2. 数据通知-回调将进入firebasemessagingservice的onMessageReceived方法中,并且当应用程序处于后台,前台或已终止状态时可以使用。

当应用程序在后台运行时,您必须使用数据通知来处理通知。


-5

我遇到了同样的问题,并重新编译了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
}

希望会有所帮助。祝好运


2
没有用的答案
Ankit Patidar

2
这是一个可怕的建议。
vsecades,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.