应用程序在后台时未调用 Firebase onMessageReceived
Firebase onMessageReceived not called when app in background
我正在使用 Firebase 并测试在应用程序处于后台时从我的服务器向我的应用程序发送通知。通知发送成功,它甚至出现在设备的通知中心,但是当通知出现时或者即使我点击它,我的 FCMessagingService 中的 onMessageReceived 方法也永远不会被调用。
当我在我的应用程序处于前台时对此进行测试时,调用了 onMessageReceived 方法并且一切正常。当应用程序在后台 运行 时出现问题。
这是有意为之的行为,还是我可以解决这个问题?
这是我的 FBMessagingService:
import android.util.Log;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
public class FBMessagingService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.i("PVL", "MESSAGE RECEIVED!!");
if (remoteMessage.getNotification().getBody() != null) {
Log.i("PVL", "RECEIVED MESSAGE: " + remoteMessage.getNotification().getBody());
} else {
Log.i("PVL", "RECEIVED MESSAGE: " + remoteMessage.getData().get("message"));
}
}
}
我遇到了这个问题(如果应用程序在后台或已关闭,应用程序不想在通知点击时打开),问题是通知正文中的 click_action
无效,请尝试将其删除或更改为有效的东西。
这是按预期工作的,仅当您的应用程序位于前台时,通知消息才会传送到您的 onMessageReceived 回调。如果您的应用程序处于后台或已关闭,则通知中心会显示一条通知消息,并且该消息中的任何 数据都会传递给因用户而启动的 intent点击通知。
您可以指定一个 click_action 来指示当用户点击通知时应启动的意图。如果没有指定 click_action,则使用主 activity。
启动 Intent 后,您可以使用
getIntent().getExtras();
检索包含随通知消息一起发送的任何数据的集合。
有关通知消息的更多信息,请参阅 docs。
我遇到了同样的问题。使用 'data message' 比 'notification' 更容易。数据消息始终加载 class onMessageReceived.
其中 class 您可以使用 notificationbuilder 创建自己的通知。
示例:
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
sendNotification(remoteMessage.getData().get("title"),remoteMessage.getData().get("body"));
}
private void sendNotification(String messageTitle,String messageBody) {
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0 /* request code */, intent,PendingIntent.FLAG_UPDATE_CURRENT);
long[] pattern = {500,500,500,500,500};
Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = (NotificationCompat.Builder) new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_stat_name)
.setContentTitle(messageTitle)
.setContentText(messageBody)
.setAutoCancel(true)
.setVibrate(pattern)
.setLights(Color.BLUE,1,1)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
}
根据 Firebase 云消息传递文档 - 如果 Activity 在前台,则将调用 onMessageReceived。如果 Activity 在后台或关闭,则通知消息会显示在应用程序启动器 activity 的通知中心。
如果您的应用程序在后台,您可以通过调用 rest 服务 api 来调用自定义的 activity 来调用 firebase 消息传递,如下所示:
URL-https://fcm.googleapis.com/fcm/send
方法类型- POST
Header- Content-Type:application/json
Authorization:key=your api key
Body/Payload:
{ "notification": {
"title": "Your Title",
"text": "Your Text",
"click_action": "OPEN_ACTIVITY_1" // should match to your intent filter
},
"data": {
"keyname": "any value " //you can get this data as extras in your activity and this data is optional
},
"to" : "to_id(firebase refreshedToken)"
}
在您的应用程序中使用此代码,您可以在要调用的 activity 中添加以下代码:
<intent-filter>
<action android:name="OPEN_ACTIVITY_1" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
默认情况下,当您的应用程序处于后台并单击通知时,您应用程序中的 Launcher Activity 将启动,如果您的通知有任何数据部分,您可以在相同的 [=14] 中处理它=]如下,
if(getIntent().getExtras()! = null){
//do your stuff
}else{
//do that you normally do
}
我遇到了同样的问题,并对此进行了更多挖掘。当应用程序在后台时,通知消息被发送到系统托盘,但是数据消息被发送到onMessageReceived()
参见 https://firebase.google.com/docs/cloud-messaging/downstream#monitor-token-generation_3
和 https://github.com/firebase/quickstart-android/blob/master/messaging/app/src/main/java/com/google/firebase/quickstart/fcm/MyFirebaseMessagingService.java
为了确保您发送的消息,文档说,“使用您的应用程序服务器和 FCM 服务器API:仅设置数据密钥。可以是可折叠的或不可折叠的可折叠。"
参见 https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages
有两种类型的消息:通知消息和数据消息。
如果您只发送数据消息,则消息字符串中没有通知对象。它会在您的应用程序在后台时被调用。
这里是关于 firebase 消息的更清晰的概念。我是从他们的支持团队那里找到的。
Firebase 具有三种消息类型:
通知消息 : 通知消息在后台或前台工作。当应用程序处于后台时,通知消息会传送到系统托盘。如果应用程序在前台,则消息由 onMessageReceived()
或 didReceiveRemoteNotification
回调处理。这些本质上就是所谓的显示消息。
数据消息:在Android平台上,数据消息可以在后台和前台工作。数据消息将由 onMessageReceived() 处理。此处的平台特定说明是:在 Android 上,可以在用于启动 activity 的 Intent 中检索数据负载。详细来说,如果您有 "click_action":"launch_Activity_1"
,您可以通过 getIntent()
仅从 Activity_1
检索此意图。
具有通知和数据负载的消息:在后台时,应用程序在通知托盘中接收通知负载,并且仅在用户点击时处理数据负载通知。在前台时,您的应用程序会收到一个消息对象,其中包含两个有效负载。其次,click_action 参数通常用于通知有效载荷而不是数据有效载荷。如果在数据负载中使用,此参数将被视为自定义键值对,因此您需要实现自定义逻辑才能使其按预期工作。
此外,我建议您使用 onMessageReceived 方法(请参阅数据消息)来提取数据包。根据你的逻辑,我检查了 bundle 对象并没有找到预期的数据内容。这是对类似案例的引用,可能会更清楚。
从服务器端,firebase 通知应采用以下格式:
服务器端应该发送"notification"对象。我的 TargetActivity
中缺少 "notification" 对象没有使用 getIntent()
收到消息。
正确的消息格式如下:
{
"data": {
"body": "here is body",
"title": "Title"
},
"notification": {
"body": "here is body",
"title": "Title",
"click_action": "YOUR_ACTION"
},
"to": "ffEseX6vwcM:APA91bF8m7wOF MY FCM ID 07j1aPUb"
}
这里是关于 firebase 消息的更清晰的概念。我是从他们的支持团队那里找到的。
有关详细信息,请访问我的 and this thread
值得强调的一点是,您必须使用数据消息 - 仅数据键 - 即使应用程序在后台时也会调用 onMessageReceived 处理程序。您的负载中不应该有任何其他通知消息键,否则如果应用程序在后台,处理程序将不会被触发。
这里提到(但在 FCM 文档中没有那么强调):
https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages
Use your app server and FCM server API: Set the data key only. Can be
either collapsible or non-collapsible.
从您的服务器请求中完全删除 notification
字段。仅发送 data
并在 onMessageReceived()
中处理它,否则当应用程序处于后台或被杀死时,您的 onMessageReceived()
将不会被触发。
不要忘记在您的通知请求中包含 "priority": "high"
字段。根据文档:数据消息以正常优先级发送,因此不会立即到达;这也可能是问题所在。
这是我从服务器发送的内容
{
"data":{
"id": 1,
"missedRequests": 5
"addAnyDataHere": 123
},
"to": "fhiT7evmZk8:APA91bFJq7Tkly4BtLRXdYvqHno2vHCRkzpJT8QZy0TlIGs......",
"priority": "high"
}
所以你可以像这样在 onMessageReceived(RemoteMessage message)
中接收你的数据....假设我必须得到 id
Object obj = message.getData().get("id");
if (obj != null) {
int id = Integer.valueOf(obj.toString());
}
我正在使用的后端使用的是通知消息,而不是数据消息。因此,在阅读了所有答案后,我尝试从发布的意图包中检索额外内容 activity。
但无论我尝试从 getIntent().getExtras();
检索哪个键,值始终为空。
不过,我终于找到了一种使用 通知消息 发送数据并从 Intent 中检索数据的方法。
此处的关键是将数据负载添加到通知消息中。
示例:
{
"data": {
"message": "message_body",
"title": "message_title"
},
"notification": {
"body": "test body",
"title": "test title"
},
"to": "E4An.."
}
执行此操作后,您将能够通过以下方式获取您的信息:
intent.getExtras().getString("title")
将是 message_title
和
intent.getExtras().getString("message")
将是 message_body
只需在 MainActivity 的 onCreate 方法中调用它即可:
if (getIntent().getExtras() != null) {
// Call your NotificationActivity here..
Intent intent = new Intent(MainActivity.this, NotificationActivity.class);
startActivity(intent);
}
如果应用程序处于后台模式或处于非活动状态(已终止),并且您在 通知 上单击,您应该检查负载在 LaunchScreen 中(在我的例子中,启动屏幕是 MainActivity.java)。
所以在 MainActivity.java 中 onCreate 检查 Extras:
if (getIntent().getExtras() != null) {
for (String key : getIntent().getExtras().keySet()) {
Object value = getIntent().getExtras().get(key);
Log.d("MainActivity: ", "Key: " + key + " Value: " + value);
}
}
如果您的问题与显示大图像有关,即如果您从 firebase 控制台发送带有图像的推送通知,并且它仅在应用程序位于前台时才显示该图像。这个问题的解决方案是发送一个只有数据字段的推送消息。像这样:
{ "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" }
此方法 handleIntent() 已弃用,因此可以按以下方式处理通知:
前台状态:通知的点击将转到您在以编程方式创建通知时提供的待定 Intent activity,因为它通常使用通知的数据负载创建.
Background/Killed 状态 - 在这里,系统本身会根据通知负载创建一个通知,单击该通知将带您到您所在应用程序的启动器 activity可以在您的任何生命周期方法中轻松获取 Intent 数据。
覆盖 FirebaseMessageService
的 handleIntent
方法对我有用。
这里是 C# (Xamarin)
中的代码
public override void HandleIntent(Intent intent)
{
try
{
if (intent.Extras != null)
{
var builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
foreach (string key in intent.Extras.KeySet())
{
builder.AddData(key, intent.Extras.Get(key).ToString());
}
this.OnMessageReceived(builder.Build());
}
else
{
base.HandleIntent(intent);
}
}
catch (Exception)
{
base.HandleIntent(intent);
}
}
这就是 Java
中的代码
public void handleIntent(Intent intent)
{
try
{
if (intent.getExtras() != null)
{
RemoteMessage.Builder builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
for (String key : intent.getExtras().keySet())
{
builder.addData(key, intent.getExtras().get(key).toString());
}
onMessageReceived(builder.build());
}
else
{
super.handleIntent(intent);
}
}
catch (Exception e)
{
super.handleIntent(intent);
}
}
根据 t3h Exi 的解决方案,我想 post 这里的干净代码。只需将其放入 MyFirebaseMessagingService,如果应用程序处于后台模式,则一切正常。您至少需要编译 com.google.firebase:firebase-messaging:10.2.1
@Override
public void handleIntent(Intent intent)
{
try
{
if (intent.getExtras() != null)
{
RemoteMessage.Builder builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
for (String key : intent.getExtras().keySet())
{
builder.addData(key, intent.getExtras().get(key).toString());
}
onMessageReceived(builder.build());
}
else
{
super.handleIntent(intent);
}
}
catch (Exception e)
{
super.handleIntent(intent);
}
}
我遇到了同样的问题。如果应用程序在前台 - 它会触发我的后台服务,我可以在其中根据通知类型更新我的数据库。
但是,该应用程序会进入后台 - 默认通知服务将小心翼翼地向用户显示通知。
这是我在后台识别应用程序并触发后台服务的解决方案,
public class FirebaseBackgroundService extends WakefulBroadcastReceiver {
private static final String TAG = "FirebaseService";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "I'm in!!!");
if (intent.getExtras() != null) {
for (String key : intent.getExtras().keySet()) {
Object value = intent.getExtras().get(key);
Log.e("FirebaseDataReceiver", "Key: " + key + " Value: " + value);
if(key.equalsIgnoreCase("gcm.notification.body") && value != null) {
Bundle bundle = new Bundle();
Intent backgroundIntent = new Intent(context, BackgroundSyncJobService.class);
bundle.putString("push_message", value + "");
backgroundIntent.putExtras(bundle);
context.startService(backgroundIntent);
}
}
}
}
}
在manifest.xml
<receiver android:exported="true" android:name=".FirebaseBackgroundService" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</receiver>
在最新的 android 8.0 版本中测试了此解决方案。谢谢
如果应用程序在后台 Fire-base 默认处理通知但是如果我们想要我们的自定义通知而不是我们必须更改我们的服务器端,它负责发送我们的自定义数据(数据有效负载)
从您的服务器请求中完全删除通知负载。仅发送数据并在 onMessageReceived() 中处理它,否则当应用程序处于后台或被杀死时,您的 onMessageReceived 将不会被触发。
现在,您的服务器端代码格式如下所示,
{
"collapse_key": "CHAT_MESSAGE_CONTACT",
"data": {
"loc_key": "CHAT_MESSAGE_CONTACT",
"loc_args": ["John Doe", "Contact Exchange"],
"text": "John Doe shared a contact in the group Contact Exchange",
"custom": {
"chat_id": 241233,
"msg_id": 123
},
"badge": 1,
"sound": "sound1.mp3",
"mute": true
}
}
注意:请参阅上面代码中的这一行
"text": "John Doe shared a contact in the group Contact Exchange"
在数据有效负载中,您应该使用 "text" 参数而不是 "body" 或 "message" 参数来描述消息或任何您想使用的文本。
onMessageReceived()
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.e(TAG, "From: " + remoteMessage.getData().toString());
if (remoteMessage == null)
return;
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
/* Log.e(TAG, "Data Payload: " + remoteMessage.getData().toString());*/
Log.e(TAG, "Data Payload: " + remoteMessage);
try {
Map<String, String> params = remoteMessage.getData();
JSONObject json = new JSONObject(params);
Log.e("JSON_OBJECT", json.toString());
Log.e(TAG, "onMessageReceived: " + json.toString());
handleDataMessage(json);
} catch (Exception e) {
Log.e(TAG, "Exception: " + e.getMessage());
}
}
}
试试这个:
public void handleIntent(Intent intent) {
try {
if (intent.getExtras() != null) {
RemoteMessage.Builder builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
for (String key : intent.getExtras().keySet()) {
builder.addData(key, intent.getExtras().get(key).toString());
}
onMessageReceived(builder.build());
} else {
super.handleIntent(intent);
}
} catch (Exception e) {
super.handleIntent(intent);
}
}
只需覆盖 FirebaseMessagingService 的 OnCreate 方法即可。当您的应用程序在后台时调用它:
public override void OnCreate()
{
// your code
base.OnCreate();
}
当收到消息并且您的应用程序处于后台时,通知将发送到主要 activity 的额外意图。
您可以在主函数的 oncreate() 或 onresume() 函数中检查额外值 activity。
您可以检查数据等字段,table 等(通知中指定的字段)
例如我使用数据作为密钥发送
public void onResume(){
super.onResume();
if (getIntent().getStringExtra("data")!=null){
fromnotification=true;
Intent i = new Intent(MainActivity.this, Activity2.class);
i.putExtra("notification","notification");
startActivity(i);
}
}
2 种类型 的 Firebase 推送通知:
1- 通知消息(显示消息) ->
-- 1.1 如果您选择此变体,如果应用程序处于 后台 ,OS 将自行创建一个通知,并将在 intent
中传递数据。然后由客户端处理这些数据。
-- 1.2 如果应用程序在 Foreground 中,那么它将通过 callback-function
在 FirebaseMessagingService
中收到通知,这取决于客户端处理它。
2- 数据消息(最多 4k 数据)-> 这些消息用于仅向客户端发送数据(静默地),并且由客户端通过回调处理这两种情况 background/foreground - FirebaseMessagingService
中的函数
这是根据官方文档:https://firebase.google.com/docs/cloud-messaging/concept-options
onMessageReceived(RemoteMessage remoteMessage) 方法基于以下情况调用。
- FCM 响应 带有通知 和数据 块:
{
"to": "device token list",
"notification": {
"body": "Body of Your Notification",
"title": "Title of Your Notification"
},
"data": {
"body": "Body of Your Notification in Data",
"title": "Title of Your Notification in Title",
"key_1": "Value for key_1",
"image_url": "www.abc.com/xyz.jpeg",
"key_2": "Value for key_2"
}
}
- 前台应用程序:
onMessageReceived(RemoteMessage remoteMessage) 调用,在通知栏中显示 LargeIcon 和 BigPicture。我们可以从 notification 和 data block
中读取内容
- 后台应用程序:
onMessageReceived(RemoteMessage remoteMessage) 未调用,系统托盘将接收消息并从 notification 读取 body 和标题阻止并在通知栏中显示默认消息和标题。
- FCM 响应 只有 数据 块:
在这种情况下,从 json
中删除 notification 块
{
"to": "device token list",
"data": {
"body": "Body of Your Notification in Data",
"title": "Title of Your Notification in Title",
"key_1": "Value for key_1",
"image_url": "www.abc.com/xyz.jpeg",
"key_2": "Value for key_2"
}
}
调用onMessageReceived()的解决方法
- 前台应用程序:
onMessageReceived(RemoteMessage remoteMessage) 调用,在通知栏中显示 LargeIcon 和 BigPicture。我们可以从 notification 和 data block
中读取内容
- 后台应用程序:
onMessageReceived(RemoteMessage remoteMessage) 调用后,系统托盘不会收到消息,因为 notification 键不在响应中。在通知栏中显示 LargeIcon 和 BigPicture
代码
private void sendNotification(Bitmap bitmap, String title, String
message, PendingIntent resultPendingIntent) {
NotificationCompat.BigPictureStyle style = new NotificationCompat.BigPictureStyle();
style.bigPicture(bitmap);
Uri defaultSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
String NOTIFICATION_CHANNEL_ID = mContext.getString(R.string.default_notification_channel_id);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "channel_name", NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(notificationChannel);
}
Bitmap iconLarge = BitmapFactory.decodeResource(mContext.getResources(),
R.drawable.mdmlogo);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.mdmlogo)
.setContentTitle(title)
.setAutoCancel(true)
.setSound(defaultSound)
.setContentText(message)
.setContentIntent(resultPendingIntent)
.setStyle(style)
.setLargeIcon(iconLarge)
.setWhen(System.currentTimeMillis())
.setPriority(Notification.PRIORITY_MAX)
.setChannelId(NOTIFICATION_CHANNEL_ID);
notificationManager.notify(1, notificationBuilder.build());
}
参考Link:
https://firebase.google.com/docs/cloud-messaging/android/receive
查看@Mahesh Kavathiya 的回答。就我而言,在服务器代码中只有这样:
{
"notification": {
"body": "here is body",
"title": "Title",
},
"to": "sdfjsdfonsdofoiewj9230idsjkfmnkdsfm"
}
您需要更改为:
{
"data": {
"body": "here is body",
"title": "Title",
"click_action": "YOUR_ACTION"
},
"notification": {
"body": "here is body",
"title": "Title"
},
"to": "sdfjsdfonsdofoiewj9230idsjkfmnkdsfm"
}
然后,如果应用程序在后台,默认的 activity 额外意图将获得 "data"
祝你好运!
我有类似的问题。根据本页中提到的答案和参考资料,以下是我如何使用以下方法解决问题的两分钱:
我之前的消息格式如下:
{
"notification": {
"title": "AppName",
"sound": null,
"body": "Hey!YouhaveaMessage"
},
"data": {
"param1": null,
"param2": [
238
],
"id": 1
},
"to": "--the device push token here--"
}
我修改了消息格式如下:
{
"data": {
"title": "AppName",
"body": "Hey! You have a message",
"param1": null,
"param2": [
238
],
"id": 1
},
"priority": "high",
"to": " — device push token here — "
}
然后我从“数据”负载本身检索了标题、body 和所有参数。这解决了问题,即使应用程序在后台,我也可以获得 OnMessageReceived 回调。
我写了一篇博客 post 解释了同样的问题,你可以找到它 here。
我可能来晚了才回答,但官方文档有点混乱。
也明确指出有两种类型的通知
- 通知消息:由FCM自动处理
- 数据消息:由客户端应用程序处理。
毫无疑问,如果服务器发送数据消息,那么 onMessageReceived() 方法肯定会被调用,但在通知消息的情况下,onMessageReceived() 方法将仅在应用程序位于前台且应用程序处于我们发送的数据的背景是空的。
示例:
假设服务器正在发送通知消息类型:
A. 前景:
- remoteMessage.data["key"] 会起作用
B. 背景情况:
-remoteMessage.data["key"] 将 return null
但是在这里,如果您在默认 activity 中找到与 getIntent().getExtras().getString("key") 相同的意图数据,则将起作用
C. kill情况下:
-remoteMessage.data["key"] 将 return null
但是在这里,如果您在默认 activity 中找到与 getIntent().getExtras().getString("key") 相同的意图数据,则将起作用
现在,假设服务器正在发送数据消息类型:
D. 前景:
- remoteMessage.data["key"] 会起作用
E. 背景情况:
- remoteMessage.data["key"] 会起作用
F.万一kill:
- remoteMessage.data["key"] 会起作用
毫无疑问,数据消息总是会调用 onMessageReceived() 方法,但如果是通知消息并且应用程序处于 background/kill 状态,您可以使用 B 的解决方案。谢谢
希望能节省大家的时间
我正在使用 Firebase 并测试在应用程序处于后台时从我的服务器向我的应用程序发送通知。通知发送成功,它甚至出现在设备的通知中心,但是当通知出现时或者即使我点击它,我的 FCMessagingService 中的 onMessageReceived 方法也永远不会被调用。
当我在我的应用程序处于前台时对此进行测试时,调用了 onMessageReceived 方法并且一切正常。当应用程序在后台 运行 时出现问题。
这是有意为之的行为,还是我可以解决这个问题?
这是我的 FBMessagingService:
import android.util.Log;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
public class FBMessagingService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.i("PVL", "MESSAGE RECEIVED!!");
if (remoteMessage.getNotification().getBody() != null) {
Log.i("PVL", "RECEIVED MESSAGE: " + remoteMessage.getNotification().getBody());
} else {
Log.i("PVL", "RECEIVED MESSAGE: " + remoteMessage.getData().get("message"));
}
}
}
我遇到了这个问题(如果应用程序在后台或已关闭,应用程序不想在通知点击时打开),问题是通知正文中的 click_action
无效,请尝试将其删除或更改为有效的东西。
这是按预期工作的,仅当您的应用程序位于前台时,通知消息才会传送到您的 onMessageReceived 回调。如果您的应用程序处于后台或已关闭,则通知中心会显示一条通知消息,并且该消息中的任何 数据都会传递给因用户而启动的 intent点击通知。
您可以指定一个 click_action 来指示当用户点击通知时应启动的意图。如果没有指定 click_action,则使用主 activity。
启动 Intent 后,您可以使用
getIntent().getExtras();
检索包含随通知消息一起发送的任何数据的集合。
有关通知消息的更多信息,请参阅 docs。
我遇到了同样的问题。使用 'data message' 比 'notification' 更容易。数据消息始终加载 class onMessageReceived.
其中 class 您可以使用 notificationbuilder 创建自己的通知。
示例:
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
sendNotification(remoteMessage.getData().get("title"),remoteMessage.getData().get("body"));
}
private void sendNotification(String messageTitle,String messageBody) {
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0 /* request code */, intent,PendingIntent.FLAG_UPDATE_CURRENT);
long[] pattern = {500,500,500,500,500};
Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = (NotificationCompat.Builder) new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_stat_name)
.setContentTitle(messageTitle)
.setContentText(messageBody)
.setAutoCancel(true)
.setVibrate(pattern)
.setLights(Color.BLUE,1,1)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
}
根据 Firebase 云消息传递文档 - 如果 Activity 在前台,则将调用 onMessageReceived。如果 Activity 在后台或关闭,则通知消息会显示在应用程序启动器 activity 的通知中心。 如果您的应用程序在后台,您可以通过调用 rest 服务 api 来调用自定义的 activity 来调用 firebase 消息传递,如下所示:
URL-https://fcm.googleapis.com/fcm/send
方法类型- POST
Header- Content-Type:application/json
Authorization:key=your api key
Body/Payload:
{ "notification": {
"title": "Your Title",
"text": "Your Text",
"click_action": "OPEN_ACTIVITY_1" // should match to your intent filter
},
"data": {
"keyname": "any value " //you can get this data as extras in your activity and this data is optional
},
"to" : "to_id(firebase refreshedToken)"
}
在您的应用程序中使用此代码,您可以在要调用的 activity 中添加以下代码:
<intent-filter>
<action android:name="OPEN_ACTIVITY_1" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
默认情况下,当您的应用程序处于后台并单击通知时,您应用程序中的 Launcher Activity 将启动,如果您的通知有任何数据部分,您可以在相同的 [=14] 中处理它=]如下,
if(getIntent().getExtras()! = null){
//do your stuff
}else{
//do that you normally do
}
我遇到了同样的问题,并对此进行了更多挖掘。当应用程序在后台时,通知消息被发送到系统托盘,但是数据消息被发送到onMessageReceived()
参见 https://firebase.google.com/docs/cloud-messaging/downstream#monitor-token-generation_3
和 https://github.com/firebase/quickstart-android/blob/master/messaging/app/src/main/java/com/google/firebase/quickstart/fcm/MyFirebaseMessagingService.java
为了确保您发送的消息,文档说,“使用您的应用程序服务器和 FCM 服务器API:仅设置数据密钥。可以是可折叠的或不可折叠的可折叠。"
参见 https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages
有两种类型的消息:通知消息和数据消息。 如果您只发送数据消息,则消息字符串中没有通知对象。它会在您的应用程序在后台时被调用。
这里是关于 firebase 消息的更清晰的概念。我是从他们的支持团队那里找到的。
Firebase 具有三种消息类型:
通知消息 : 通知消息在后台或前台工作。当应用程序处于后台时,通知消息会传送到系统托盘。如果应用程序在前台,则消息由 onMessageReceived()
或 didReceiveRemoteNotification
回调处理。这些本质上就是所谓的显示消息。
数据消息:在Android平台上,数据消息可以在后台和前台工作。数据消息将由 onMessageReceived() 处理。此处的平台特定说明是:在 Android 上,可以在用于启动 activity 的 Intent 中检索数据负载。详细来说,如果您有 "click_action":"launch_Activity_1"
,您可以通过 getIntent()
仅从 Activity_1
检索此意图。
具有通知和数据负载的消息:在后台时,应用程序在通知托盘中接收通知负载,并且仅在用户点击时处理数据负载通知。在前台时,您的应用程序会收到一个消息对象,其中包含两个有效负载。其次,click_action 参数通常用于通知有效载荷而不是数据有效载荷。如果在数据负载中使用,此参数将被视为自定义键值对,因此您需要实现自定义逻辑才能使其按预期工作。
此外,我建议您使用 onMessageReceived 方法(请参阅数据消息)来提取数据包。根据你的逻辑,我检查了 bundle 对象并没有找到预期的数据内容。这是对类似案例的引用,可能会更清楚。
从服务器端,firebase 通知应采用以下格式:
服务器端应该发送"notification"对象。我的 TargetActivity
中缺少 "notification" 对象没有使用 getIntent()
收到消息。
正确的消息格式如下:
{
"data": {
"body": "here is body",
"title": "Title"
},
"notification": {
"body": "here is body",
"title": "Title",
"click_action": "YOUR_ACTION"
},
"to": "ffEseX6vwcM:APA91bF8m7wOF MY FCM ID 07j1aPUb"
}
这里是关于 firebase 消息的更清晰的概念。我是从他们的支持团队那里找到的。
有关详细信息,请访问我的
值得强调的一点是,您必须使用数据消息 - 仅数据键 - 即使应用程序在后台时也会调用 onMessageReceived 处理程序。您的负载中不应该有任何其他通知消息键,否则如果应用程序在后台,处理程序将不会被触发。
这里提到(但在 FCM 文档中没有那么强调):
https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages
Use your app server and FCM server API: Set the data key only. Can be either collapsible or non-collapsible.
从您的服务器请求中完全删除 notification
字段。仅发送 data
并在 onMessageReceived()
中处理它,否则当应用程序处于后台或被杀死时,您的 onMessageReceived()
将不会被触发。
不要忘记在您的通知请求中包含 "priority": "high"
字段。根据文档:数据消息以正常优先级发送,因此不会立即到达;这也可能是问题所在。
这是我从服务器发送的内容
{
"data":{
"id": 1,
"missedRequests": 5
"addAnyDataHere": 123
},
"to": "fhiT7evmZk8:APA91bFJq7Tkly4BtLRXdYvqHno2vHCRkzpJT8QZy0TlIGs......",
"priority": "high"
}
所以你可以像这样在 onMessageReceived(RemoteMessage message)
中接收你的数据....假设我必须得到 id
Object obj = message.getData().get("id");
if (obj != null) {
int id = Integer.valueOf(obj.toString());
}
我正在使用的后端使用的是通知消息,而不是数据消息。因此,在阅读了所有答案后,我尝试从发布的意图包中检索额外内容 activity。
但无论我尝试从 getIntent().getExtras();
检索哪个键,值始终为空。
不过,我终于找到了一种使用 通知消息 发送数据并从 Intent 中检索数据的方法。
此处的关键是将数据负载添加到通知消息中。
示例:
{
"data": {
"message": "message_body",
"title": "message_title"
},
"notification": {
"body": "test body",
"title": "test title"
},
"to": "E4An.."
}
执行此操作后,您将能够通过以下方式获取您的信息:
intent.getExtras().getString("title")
将是 message_title
和
intent.getExtras().getString("message")
将是 message_body
只需在 MainActivity 的 onCreate 方法中调用它即可:
if (getIntent().getExtras() != null) {
// Call your NotificationActivity here..
Intent intent = new Intent(MainActivity.this, NotificationActivity.class);
startActivity(intent);
}
如果应用程序处于后台模式或处于非活动状态(已终止),并且您在 通知 上单击,您应该检查负载在 LaunchScreen 中(在我的例子中,启动屏幕是 MainActivity.java)。
所以在 MainActivity.java 中 onCreate 检查 Extras:
if (getIntent().getExtras() != null) {
for (String key : getIntent().getExtras().keySet()) {
Object value = getIntent().getExtras().get(key);
Log.d("MainActivity: ", "Key: " + key + " Value: " + value);
}
}
如果您的问题与显示大图像有关,即如果您从 firebase 控制台发送带有图像的推送通知,并且它仅在应用程序位于前台时才显示该图像。这个问题的解决方案是发送一个只有数据字段的推送消息。像这样:
{ "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" }
此方法 handleIntent() 已弃用,因此可以按以下方式处理通知:
前台状态:通知的点击将转到您在以编程方式创建通知时提供的待定 Intent activity,因为它通常使用通知的数据负载创建.
Background/Killed 状态 - 在这里,系统本身会根据通知负载创建一个通知,单击该通知将带您到您所在应用程序的启动器 activity可以在您的任何生命周期方法中轻松获取 Intent 数据。
覆盖 FirebaseMessageService
的 handleIntent
方法对我有用。
这里是 C# (Xamarin)
中的代码public override void HandleIntent(Intent intent)
{
try
{
if (intent.Extras != null)
{
var builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
foreach (string key in intent.Extras.KeySet())
{
builder.AddData(key, intent.Extras.Get(key).ToString());
}
this.OnMessageReceived(builder.Build());
}
else
{
base.HandleIntent(intent);
}
}
catch (Exception)
{
base.HandleIntent(intent);
}
}
这就是 Java
中的代码public void handleIntent(Intent intent)
{
try
{
if (intent.getExtras() != null)
{
RemoteMessage.Builder builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
for (String key : intent.getExtras().keySet())
{
builder.addData(key, intent.getExtras().get(key).toString());
}
onMessageReceived(builder.build());
}
else
{
super.handleIntent(intent);
}
}
catch (Exception e)
{
super.handleIntent(intent);
}
}
根据 t3h Exi 的解决方案,我想 post 这里的干净代码。只需将其放入 MyFirebaseMessagingService,如果应用程序处于后台模式,则一切正常。您至少需要编译 com.google.firebase:firebase-messaging:10.2.1
@Override
public void handleIntent(Intent intent)
{
try
{
if (intent.getExtras() != null)
{
RemoteMessage.Builder builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
for (String key : intent.getExtras().keySet())
{
builder.addData(key, intent.getExtras().get(key).toString());
}
onMessageReceived(builder.build());
}
else
{
super.handleIntent(intent);
}
}
catch (Exception e)
{
super.handleIntent(intent);
}
}
我遇到了同样的问题。如果应用程序在前台 - 它会触发我的后台服务,我可以在其中根据通知类型更新我的数据库。 但是,该应用程序会进入后台 - 默认通知服务将小心翼翼地向用户显示通知。
这是我在后台识别应用程序并触发后台服务的解决方案,
public class FirebaseBackgroundService extends WakefulBroadcastReceiver {
private static final String TAG = "FirebaseService";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "I'm in!!!");
if (intent.getExtras() != null) {
for (String key : intent.getExtras().keySet()) {
Object value = intent.getExtras().get(key);
Log.e("FirebaseDataReceiver", "Key: " + key + " Value: " + value);
if(key.equalsIgnoreCase("gcm.notification.body") && value != null) {
Bundle bundle = new Bundle();
Intent backgroundIntent = new Intent(context, BackgroundSyncJobService.class);
bundle.putString("push_message", value + "");
backgroundIntent.putExtras(bundle);
context.startService(backgroundIntent);
}
}
}
}
}
在manifest.xml
<receiver android:exported="true" android:name=".FirebaseBackgroundService" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</receiver>
在最新的 android 8.0 版本中测试了此解决方案。谢谢
如果应用程序在后台 Fire-base 默认处理通知但是如果我们想要我们的自定义通知而不是我们必须更改我们的服务器端,它负责发送我们的自定义数据(数据有效负载)
从您的服务器请求中完全删除通知负载。仅发送数据并在 onMessageReceived() 中处理它,否则当应用程序处于后台或被杀死时,您的 onMessageReceived 将不会被触发。
现在,您的服务器端代码格式如下所示,
{
"collapse_key": "CHAT_MESSAGE_CONTACT",
"data": {
"loc_key": "CHAT_MESSAGE_CONTACT",
"loc_args": ["John Doe", "Contact Exchange"],
"text": "John Doe shared a contact in the group Contact Exchange",
"custom": {
"chat_id": 241233,
"msg_id": 123
},
"badge": 1,
"sound": "sound1.mp3",
"mute": true
}
}
注意:请参阅上面代码中的这一行
"text": "John Doe shared a contact in the group Contact Exchange"
在数据有效负载中,您应该使用 "text" 参数而不是 "body" 或 "message" 参数来描述消息或任何您想使用的文本。
onMessageReceived()
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.e(TAG, "From: " + remoteMessage.getData().toString());
if (remoteMessage == null)
return;
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
/* Log.e(TAG, "Data Payload: " + remoteMessage.getData().toString());*/
Log.e(TAG, "Data Payload: " + remoteMessage);
try {
Map<String, String> params = remoteMessage.getData();
JSONObject json = new JSONObject(params);
Log.e("JSON_OBJECT", json.toString());
Log.e(TAG, "onMessageReceived: " + json.toString());
handleDataMessage(json);
} catch (Exception e) {
Log.e(TAG, "Exception: " + e.getMessage());
}
}
}
试试这个:
public void handleIntent(Intent intent) {
try {
if (intent.getExtras() != null) {
RemoteMessage.Builder builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
for (String key : intent.getExtras().keySet()) {
builder.addData(key, intent.getExtras().get(key).toString());
}
onMessageReceived(builder.build());
} else {
super.handleIntent(intent);
}
} catch (Exception e) {
super.handleIntent(intent);
}
}
只需覆盖 FirebaseMessagingService 的 OnCreate 方法即可。当您的应用程序在后台时调用它:
public override void OnCreate()
{
// your code
base.OnCreate();
}
当收到消息并且您的应用程序处于后台时,通知将发送到主要 activity 的额外意图。
您可以在主函数的 oncreate() 或 onresume() 函数中检查额外值 activity。
您可以检查数据等字段,table 等(通知中指定的字段)
例如我使用数据作为密钥发送
public void onResume(){
super.onResume();
if (getIntent().getStringExtra("data")!=null){
fromnotification=true;
Intent i = new Intent(MainActivity.this, Activity2.class);
i.putExtra("notification","notification");
startActivity(i);
}
}
2 种类型 的 Firebase 推送通知:
1- 通知消息(显示消息) ->
-- 1.1 如果您选择此变体,如果应用程序处于 后台 ,OS 将自行创建一个通知,并将在 intent
中传递数据。然后由客户端处理这些数据。
-- 1.2 如果应用程序在 Foreground 中,那么它将通过 callback-function
在 FirebaseMessagingService
中收到通知,这取决于客户端处理它。
2- 数据消息(最多 4k 数据)-> 这些消息用于仅向客户端发送数据(静默地),并且由客户端通过回调处理这两种情况 background/foreground - FirebaseMessagingService
这是根据官方文档:https://firebase.google.com/docs/cloud-messaging/concept-options
onMessageReceived(RemoteMessage remoteMessage) 方法基于以下情况调用。
- FCM 响应 带有通知 和数据 块:
{
"to": "device token list",
"notification": {
"body": "Body of Your Notification",
"title": "Title of Your Notification"
},
"data": {
"body": "Body of Your Notification in Data",
"title": "Title of Your Notification in Title",
"key_1": "Value for key_1",
"image_url": "www.abc.com/xyz.jpeg",
"key_2": "Value for key_2"
}
}
- 前台应用程序:
onMessageReceived(RemoteMessage remoteMessage) 调用,在通知栏中显示 LargeIcon 和 BigPicture。我们可以从 notification 和 data block
中读取内容- 后台应用程序:
onMessageReceived(RemoteMessage remoteMessage) 未调用,系统托盘将接收消息并从 notification 读取 body 和标题阻止并在通知栏中显示默认消息和标题。
- FCM 响应 只有 数据 块:
在这种情况下,从 json
中删除 notification 块{
"to": "device token list",
"data": {
"body": "Body of Your Notification in Data",
"title": "Title of Your Notification in Title",
"key_1": "Value for key_1",
"image_url": "www.abc.com/xyz.jpeg",
"key_2": "Value for key_2"
}
}
调用onMessageReceived()的解决方法
- 前台应用程序:
onMessageReceived(RemoteMessage remoteMessage) 调用,在通知栏中显示 LargeIcon 和 BigPicture。我们可以从 notification 和 data block
中读取内容- 后台应用程序:
onMessageReceived(RemoteMessage remoteMessage) 调用后,系统托盘不会收到消息,因为 notification 键不在响应中。在通知栏中显示 LargeIcon 和 BigPicture
代码
private void sendNotification(Bitmap bitmap, String title, String
message, PendingIntent resultPendingIntent) {
NotificationCompat.BigPictureStyle style = new NotificationCompat.BigPictureStyle();
style.bigPicture(bitmap);
Uri defaultSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
String NOTIFICATION_CHANNEL_ID = mContext.getString(R.string.default_notification_channel_id);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "channel_name", NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(notificationChannel);
}
Bitmap iconLarge = BitmapFactory.decodeResource(mContext.getResources(),
R.drawable.mdmlogo);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.mdmlogo)
.setContentTitle(title)
.setAutoCancel(true)
.setSound(defaultSound)
.setContentText(message)
.setContentIntent(resultPendingIntent)
.setStyle(style)
.setLargeIcon(iconLarge)
.setWhen(System.currentTimeMillis())
.setPriority(Notification.PRIORITY_MAX)
.setChannelId(NOTIFICATION_CHANNEL_ID);
notificationManager.notify(1, notificationBuilder.build());
}
参考Link:
https://firebase.google.com/docs/cloud-messaging/android/receive
查看@Mahesh Kavathiya 的回答。就我而言,在服务器代码中只有这样:
{
"notification": {
"body": "here is body",
"title": "Title",
},
"to": "sdfjsdfonsdofoiewj9230idsjkfmnkdsfm"
}
您需要更改为:
{
"data": {
"body": "here is body",
"title": "Title",
"click_action": "YOUR_ACTION"
},
"notification": {
"body": "here is body",
"title": "Title"
},
"to": "sdfjsdfonsdofoiewj9230idsjkfmnkdsfm"
}
然后,如果应用程序在后台,默认的 activity 额外意图将获得 "data"
祝你好运!
我有类似的问题。根据本页中提到的答案和参考资料,以下是我如何使用以下方法解决问题的两分钱:
我之前的消息格式如下:
{
"notification": {
"title": "AppName",
"sound": null,
"body": "Hey!YouhaveaMessage"
},
"data": {
"param1": null,
"param2": [
238
],
"id": 1
},
"to": "--the device push token here--"
}
我修改了消息格式如下:
{
"data": {
"title": "AppName",
"body": "Hey! You have a message",
"param1": null,
"param2": [
238
],
"id": 1
},
"priority": "high",
"to": " — device push token here — "
}
然后我从“数据”负载本身检索了标题、body 和所有参数。这解决了问题,即使应用程序在后台,我也可以获得 OnMessageReceived 回调。 我写了一篇博客 post 解释了同样的问题,你可以找到它 here。
我可能来晚了才回答,但官方文档有点混乱。
也明确指出有两种类型的通知
- 通知消息:由FCM自动处理
- 数据消息:由客户端应用程序处理。
毫无疑问,如果服务器发送数据消息,那么 onMessageReceived() 方法肯定会被调用,但在通知消息的情况下,onMessageReceived() 方法将仅在应用程序位于前台且应用程序处于我们发送的数据的背景是空的。
示例:
假设服务器正在发送通知消息类型:
A. 前景:
- remoteMessage.data["key"] 会起作用
B. 背景情况: -remoteMessage.data["key"] 将 return null 但是在这里,如果您在默认 activity 中找到与 getIntent().getExtras().getString("key") 相同的意图数据,则将起作用
C. kill情况下: -remoteMessage.data["key"] 将 return null 但是在这里,如果您在默认 activity 中找到与 getIntent().getExtras().getString("key") 相同的意图数据,则将起作用
现在,假设服务器正在发送数据消息类型:
D. 前景:
- remoteMessage.data["key"] 会起作用
E. 背景情况:
- remoteMessage.data["key"] 会起作用
F.万一kill:
- remoteMessage.data["key"] 会起作用
毫无疑问,数据消息总是会调用 onMessageReceived() 方法,但如果是通知消息并且应用程序处于 background/kill 状态,您可以使用 B 的解决方案。谢谢
希望能节省大家的时间