如何处理通知时,应用程序在后台的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代码。如果我的应用程序在前台运行,而不是在后台运行,就会调用这个函数。我怎么能运行这段代码时,应用程序是在后台太?

// [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]
566286 次浏览

根据文档

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

当你的应用程序在后台时,Android会引导通知 发送到系统托盘的消息。用户轻按通知打开

.应用程序默认启动

这包括同时包含通知和数据的消息 有效载荷。在这些情况下,通知被传递到设备的 系统托盘,而数据有效载荷是在附加的

如果你想打开你的应用程序并执行特定的操作,设置 并将其映射到一个意图 过滤器在你想要启动的活动。例如,set click_action到OPEN_ACTIVITY_1来触发一个意图过滤器 : < / p >

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

编辑:

基于线程:

不能使用Firebase Console设置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. 显示消息:只有当你的应用在前景中,这些消息才会触发onMessageReceived()回调
  2. 数据信息:如果你的应用在前景/背景/死亡中,这些消息会触发onMessageReceived()回调甚至
注意: Firebase团队还没有开发一个UI来发送data-messages 还有你的设备。您应该使用您的服务器发送这种类型!< / p >
< p > < br >

2. 如何?

为了实现这一点,你必须对以下URL执行POST请求:

# EYZ0 # EYZ1

  • # eyz2 # eyz0, # eyz3 # eyz1
  • # eyz2 # eyz0, # eyz3 # eyz1

主体使用主题

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

< br >

要确保你是不添加 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
}

要使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
*/
}

根据使用firebase发送到下游中的firebase文档,有两种类型的有效载荷:

  1. < p > # EYZ0

    该参数指定消息有效负载的自定义键值对。 客户端应用程序负责处理数据消息。数据消息只有自定义的键值对 李< /引用> < / >

  2. < p > # EYZ0

    此参数指定通知有效负载的预定义的、用户可见的键-值对。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,并将其映射到你想要启动的Activity中的意图过滤器。例如,将click_action设置为OPEN_ACTIVITY_1来触发一个意图过滤器,如下所示:

<intent-filter>
<action android:name="OPEN_ACTIVITY_1" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
把intent-filter放在你的manifest上,在你的activity标签里面。当你点击通知时,它将打开应用程序并直接进入你在click_action中定义的活动,在本例中为“OPEN_ACTIVTY_1”。 在这个活动中,你可以通过

获取数据
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消息的更清晰的概念。我从他们的支援组找到的。

# EYZ0:

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

数据信息:在Android平台上,数据消息可以在后台和前台工作。数据消息将由onMessageReceived()处理。在Android上,数据有效载荷可以在用于启动你的活动的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()
    

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

在这个if块中,根据firebase UI搜索你的密钥。

在这个例子中,我的键和值如上所示;(对不起,语言=)) # EYZ0 < / p >

当我的代码工作时,我得到“com.rda.note”。

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

有了这行代码,我关闭了我的应用程序,打开谷歌播放市场

快乐编码=)

即使应用程序在后台和前台,发送消息的简单方法如下 要使用API发送消息,您可以使用一个名为AdvancedREST客户端工具,它是一个chrome扩展,并发送带有以下参数的消息

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

使用这个url:- https://fcm.googleapis.com/fcm/send 内容类型:application / json 授权:key=您的服务器密钥来自或授权密钥(参见下面的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"
}

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

并且您需要将接收者的tokenID放在使用API发送的POST请求的“to”部分。

因为display-messages是从Firebase通知UI发送的,只有当你的应用程序在前台时才有效。对于data-messages,需要对FCM进行POST调用

步骤

  1. 安装高级休息客户端谷歌Chrome扩展 李# EYZ0 < / p > < / >

  2. 添加以下标题

    关键: Content-Type, 价值: application/json

    关键:授权,价值:密钥="您的服务器密钥" 李# EYZ0 < / p > < / >

  3. 添加正文

    • 如果使用主题:

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

我觉得所有的响应都是不完整的,但它们都有一些你需要处理的东西,当你的应用程序在后台时,一个有数据的通知。

遵循这些步骤,你将能够在应用程序处于后台时处理通知。

  1. 添加intent-filter,如下所示:

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

到要处理通知数据的活动。

  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中添加的带有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日

当你的应用在背景, Android 将通知消息定向到系统托盘。用户轻按 通知打开默认应用程序启动器.

. # 这包括包含通知和数据有效负载的消息 (以及从Notifications控制台发送的所有消息)。在这些 在情况下,通知被传递到设备的系统托盘,和 数据有效负载是在您的意图的附加部分中交付的 发射器活动。< / p >

所以,你应该同时使用有效负载通知和数据:

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

不需要使用click_action。你应该在发射器的活动上获得额外的意图

<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通知控制台的数据。不要忘记在高级选项部分填写自定义数据字段

@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句柄本身现在你有了这个方法 无论你想做什么…你可以在这里用这个方法.....< / p >

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

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

//做任何你想做的.... } < / p >

通常这是我们在通知中从服务器获得的结构

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

这是由你决定的,你想给数据键或你想给任何通知,你可以给....... 无论你在这里给出什么,用相同的键,你都会得到数据.........< / p >

有少数情况下,如果你没有发送点击动作在这种情况下,当你将点击通知默认活动将打开,但如果你想打开你的特定活动时,应用程序是在后台,你可以从这个handleIntent方法调用你的活动,因为这是每次调用

2017年更新答案

以下是文档对此的明确回答:

enter image description here

我想出了各种方案

当应用程序在前景onmessagerreceived ()方法从FirebaseService调用。因此将调用服务类中定义的pendingIntent

当app在背景第一个活动被调用。

现在,如果你使用启动活动,那么必须记住splashactivity将被调用,否则如果没有splashActivity,那么无论第一个活动是什么,都会被调用。

然后你需要检查firstActivitygetIntent (),看看它是否有任何。如果一切正常,你会看到bundle是有值填充的。如果从服务器发送的数据标签中的值如下所示,

"data": {
"user_name": "arefin sajib",
"value": "user name notification"
}
然后在第一个活动中,你会看到, 有一个有效的意图(getIntent()不是空的),有效的捆绑包和内部捆绑包,将有上面提到的整个JSON与数据作为< em > < / em >键

在这种情况下,提取值的代码是这样的,

    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库,阻止它在应用程序处于后台时发送通知

< p > *库 # EYZ0 < / p >
 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
}

希望有帮助。祝你好运

感谢你们所有人的回答。但是我通过发送数据信息而不是通知来解决这个问题。 服务器代码< / p >
<?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;


?>

并在onmessagerreceived中捕获数据

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月答案:

你必须确保消息中没有“通知”关键字。只包括“data”,应用程序将能够处理onmessagerreceived中的消息,即使在后台或已杀死。

使用云功能:

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

然后在onmessagerreceived()中,在你的类中扩展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'键和对象是添加到推送通知包。所以当App处于后台或被杀死时,你将不会收到详细的推送通知

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

在这里,你将添加'data'键到你的推送包中。因此,详细的推送将如预期的那样显示出来。

使用这段代码,你可以在后台/前台获得通知,也可以放置动作:

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

你要工作onmessagerreceived (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());

# EYZ0

由于FCM现在使用OAUTH 2,在这种情况下将会有认证问题

所以我阅读了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"
}
}
}

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

现在让我们用我们的代币(它只有效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请求中使用这个令牌。然后我发布我的数据消息,它现在由我的应用程序onmessagerecreceived函数处理。

自2019年以来,谷歌Firebase的api有了很大的变化 我的意思是: # EYZ0 < / p >

在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

经过许多小时的研究,通过所有其他StackOverflow问题和答案,并尝试无数过时的解决方案,这个解决方案设法在以下3个场景中显示通知:

< p > # EYZ0 < br > 通知是由我的MyFirebaseMessagingService类的onmessagerreceived方法接收的

-应用程序已被杀死(它不在后台运行): FCM自动将通知发送到通知托盘。当用户触摸通知时,应用程序通过调用manifest中有android.intent.category.LAUNCHER的活动来启动。你可以在onCreate()方法中使用getIntent(). getextras()来获得通知的数据部分

应用程序在后台: FCM自动将通知发送到通知托盘。当用户触摸通知时,应用程序通过启动manifest中包含android.intent.category.LAUNCHER的活动被带到前台。由于我的应用程序在该活动中有launchMode="singleTop", onCreate()方法没有被调用,因为已经创建了同一类的一个活动,相反,该类的onNewIntent()方法被调用,您通过使用intent.getExtras()获得通知的数据部分 < p >步骤: 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控制台发送通知:

通知部分: 通知标题=在通知对话框中显示的标题(可选) 通知文本=显示给用户的消息(必需) 然后在Target部分: App =你的Android应用 和“附加选项”部分: Android通知通道= default_channel_id 自定义数据 键:标题值:(这里的文本与通知部分的标题字段相同) key: body value:(这里的文本与Notification部分的Message字段相同) 键:click_action值:.MainActivity 声音=禁用< br > < / p > = 4周到期

你可以在模拟器中调试它与API 28谷歌播放。

编码快乐!

我也有同样的问题。经过一些挖掘为什么我的MainActivity被调用意图没有数据,我意识到我的发射器活动(如在Manifest)是SplashActivity。在那里我找到了消息数据,并将它们转发给MainActivity。效果很好。我相信这能帮助到一些人。

谢谢你的回答。

在一般情况下

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

  • 只有当你的应用在前景中,这些消息才会触发onmessagerreceived()回调

  • 这些消息触发onmessagerreceived()回调,即使你的应用程序在前景/背景/死亡

# EYZ0例子:

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

# EYZ0例子:

 {
"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的细节,你可以在这里找到:在Android上设置一个Firebase云消息客户端应用程序

提供的解决方案对我的情况不起作用。

最后,我发现一些电池优化应用程序允许用户控制应用程序发送通知。在我的例子中,三星的Smart Manager在我的应用被从最近列表中删除后就自动阻止了它。

关闭我的应用程序的这个功能是我能找到的唯一解决办法。

我在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论坛上发现了这个,这对我来说很有用!如果这对你有用,请告诉我。

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

创建一个Broadcast并从service worker中以Broadcast Message的形式发送有效负载。

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

为了能够从应用程序在后台发送的firebase通知中检索数据,您需要在通知数据集中添加click_action条目。

在firebase控制台设置额外的通知选项,如下所示:(你必须包含任何你想在你的应用程序中检索的额外数据): # EYZ0 < / p >

并包括意图过滤器在你的manifest文件下的活动要启动

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

然后在活动onNewIntent中获取bundle数据:

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

当你的应用程序在前台时,你可以像这样设置onMessageReceived:

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