情况1:应用程序前台 -> 重写“onMessageReceived()”以创建您的自定义通知。
情况2:应用程序后台 -> 系统将直接创建通知,我们不需要也无法做任何事情,因为这种情况下不会触发“onMessageReceived()”。
然而,如果应用程序在后台时我什么都不能做,我就无法创建自定义通知。(例如,用户点击通知后,它将弹出一个窗口显示详细信息。)那么在应用程序后台时如何处理使用FCM的通知呢?
有一个坏消息。
谷歌在版本“com.google.firebase:firebase-messaging:11.6.0”中更改了Firebase源代码。
handleIntent现在是“public final void method”。这意味着我们无法覆盖它。
如果您想使用解决方案,请将版本更改为“com.google.firebase:firebase-messaging:11.4.2”
尝试我的方法。它可以在项目构建版本为Android 6.0以上(API级别23)时完美工作,我已经尝试过了。
有比官方网站教程更好的方法
官方网站说,当应用程序处于后台时,通知将由系统创建。因此,您无法通过覆盖“onMessageReceived()”来处理它。因为“onMessageReceived()”仅在应用程序处于前台时触发。
但事实并非如此。实际上,Firebase库会在应用程序处于后台时创建通知。
在追踪Firebase库代码之后,我找到了一种更好的方法。
步骤1. 覆盖FirebaseMessagingService中的“handleIntent()”而不是“onMessageReceived()”
为什么:
因为该方法将在应用程序处于前台或后台时触发。因此,我们可以在两种情况下处理FCM消息并创建自定义通知。
@Override
public void handleIntent(Intent intent) {
Log.d( "FCM", "handleIntent ");
}
Bundle bundle = intent.getExtras();
if (bundle != null) {
for (String key : bundle.keySet()) {
Object value = bundle.get(key);
Log.d("FCM", "Key: " + key + " Value: " + value);
}
}
private void removeFirebaseOrigianlNotificaitons() {
//check notificationManager is available
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager == null )
return;
//check api level for getActiveNotifications()
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
//if your Build version is less than android 6.0
//we can remove all notifications instead.
//notificationManager.cancelAll();
return;
}
//check there are notifications
StatusBarNotification[] activeNotifications =
notificationManager.getActiveNotifications();
if (activeNotifications == null)
return;
//remove all notification created by library(super.handleIntent(intent))
for (StatusBarNotification tmp : activeNotifications) {
Log.d("FCM StatusBarNotification",
"StatusBarNotification tag/id: " + tmp.getTag() + " / " + tmp.getId());
String tag = tmp.getTag();
int id = tmp.getId();
//trace the library source code, follow the rule to remove it.
if (tag != null && tag.contains("FCM-Notification"))
notificationManager.cancel(tag, id);
}
}
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static int notificationCount=0;
@Override
public void handleIntent(Intent intent) {
//add a log, and you'll see the method will be triggered all the time (both foreground and background).
Log.d( "FCM", "handleIntent");
//if you don't know the format of your FCM message,
//just print it out, and you'll know how to parse it
Bundle bundle = intent.getExtras();
if (bundle != null) {
for (String key : bundle.keySet()) {
Object value = bundle.get(key);
Log.d("FCM", "Key: " + key + " Value: " + value);
}
}
//the background notification is created by super method
//but you can't remove the super method.
//the super method do other things, not just creating the notification
super.handleIntent(intent);
//remove the Notificaitons
removeFirebaseOrigianlNotificaitons();
if (bundle ==null)
return;
//pares the message
CloudMsg cloudMsg = parseCloudMsg(bundle);
//if you want take the data to Activity, set it
Bundle myBundle = new Bundle();
myBundle.putSerializable(TYPE_FCM_PLATFORM, cloudMsg);
Intent myIntent = new Intent(this, NotificationActivity.class);
myIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
myIntent.putExtras(myBundle);
PendingIntent pendingIntent = PendingIntent.getActivity(this, notificationCount, myIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//set the Notification
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.icon)
.setContentTitle(cloudMsg.getTitle())
.setContentText(cloudMsg.getMessage())
.setAutoCancel(true)
.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(notificationCount++, notificationBuilder.build());
}
/**
* parse the message which is from FCM
* @param bundle
*/
private CloudMsg parseCloudMsg(Bundle bundle) {
String title = null, msg=null;
//if the message is sent from Firebase platform, the key will be that
msg = (String) bundle.get("gcm.notification.body");
if(bundle.containsKey("gcm.notification.title"))
title = (String) bundle.get("gcm.notification.title");
//parse your custom message
String testValue=null;
testValue = (String) bundle.get("testKey");
//package them into a object(CloudMsg is your own structure), it is easy to send to Activity.
CloudMsg cloudMsg = new CloudMsg(title, msg, testValue);
return cloudMsg;
}
/**
* remove the notification created by "super.handleIntent(intent)"
*/
private void removeFirebaseOrigianlNotificaitons() {
//check notificationManager is available
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager == null )
return;
//check api level for getActiveNotifications()
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
//if your Build version is less than android 6.0
//we can remove all notifications instead.
//notificationManager.cancelAll();
return;
}
//check there are notifications
StatusBarNotification[] activeNotifications =
notificationManager.getActiveNotifications();
if (activeNotifications == null)
return;
//remove all notification created by library(super.handleIntent(intent))
for (StatusBarNotification tmp : activeNotifications) {
Log.d("FCM StatusBarNotification",
"tag/id: " + tmp.getTag() + " / " + tmp.getId());
String tag = tmp.getTag();
int id = tmp.getId();
//trace the library source code, follow the rule to remove it.
if (tag != null && tag.contains("FCM-Notification"))
notificationManager.cancel(tag, id);
}
}
}
{
"to": "topic_name",
"priority": "high",
"data": {
"field1": "field1 value"
"field2": "field2 value"
}
"notification" : {
"body" : "Lorem ipsum",
"title" : "sampke title"
"click_action": "SHOW_DETAILS"
}
}
data
负载是您希望在用户单击通知后显示为消息详细信息的实际数据,notification
负载表示如何生成通知应该看起来(有更多属性可以设置),您不需要自己构建通知,只需要在此处设置其属性。
要在用户点击通知后显示您的活动,您需要设置与click_action
相对应的意图过滤器:
<intent-filter>
<action android:name="SHOW_DETAILS"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
onCreate
方法中执行以下操作:Bundle bundle = getIntent().getExtras();
if(bundle.getString("action").equals("SHOW_DETAILS")) /*This indicates activity is launched from notification, not directly*/
{
//Data retrieved from notification payload send
String filed1 = bundle.getString("field1");
String filed2 = bundle.getString("field2");
}
onMessageReceived()
事件,因此您可以在那里处理相同的数据(我猜您知道如何处理)。https://firebase.google.com/docs/cloud-messaging/http-server-ref https://github.com/firebase/quickstart-android/tree/master/messaging
onMessageReceived()
中接收它:https://stackoverflow.com/questions/48301350/android-notifications-when-app-is-in-background/48301893#48301893 - Peter HaddadonMessageReceived
方法,因此您可以处理数据并显示自定义通知。
https://firebase.google.com/docs/cloud-messaging/android/receive
从服务器发送的数据消息格式:{"message":{
"token":"Your Device Token",
"data":{
"Nick" : "Mario",
"body" : "great match!",
"Room" : "PortugalVSDenmark"
}
}
}
FCM不会再在应用程序被杀死时发送后台通知,而且正如您在答案中所描述的handleIntent()
解决方案可能适用于某些设备和某些旧版本的FCM,此外,如果您@override
方法在firebase的官方文档中没有描述,您可能会在此遇到一些问题,并且在使用时需自行承担风险!。
那么解决方案是什么呢?
您需要在FCM之外使用您自己的推送通知服务,例如Telegram。
或者使用SyncAdapter作为GCM的替代品,例如Gmail。
因此,如果您需要像这些应用程序一样成功地工作,则必须使用您自己的hack。
public class FirebaseMessageReceiver extends FirebaseMessagingService{
private static final String TAG = "main";
String s12;
String channel_id = "general";
Intent intent;
@Override
public void onNewToken(@NonNull String token)
{
Log.d(TAG, "Refreshed token: " + token);
}
@Override
public void
onMessageReceived(RemoteMessage remoteMessage) {
s12=remoteMessage.getNotification().getClickAction();
Log.d("tttt",(remoteMessage.getData().toString()));
Log.d("ttttttt",(remoteMessage.getNotification().toString()));
if (remoteMessage.getNotification() != null) {
showNotification(remoteMessage.getNotification().getTitle(), remoteMessage.getNotification().getBody());
}
//
}
public void handleIntent(Intent intent)
{
try
{
if (intent.getExtras() != null)
{
RemoteMessage.Builder builder = new RemoteMessage.Builder("FirebaseMessageReceiver");
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);
}
}
private RemoteViews getCustomDesign(String title, String message) {
RemoteViews remoteViews = new RemoteViews(getApplicationContext().getPackageName(), R.layout.notification);
remoteViews.setTextViewText(R.id.title111, title);
remoteViews.setTextViewText(R.id.message111, message);
remoteViews.setImageViewResource(R.id.icon111, R.drawable.favicon);
return remoteViews;
}
// Method to display the notifications
public void showNotification(String title, String message) {
intent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse(s12));
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent notifyIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
Log.d("notifyy",notifyIntent.toString());
NotificationCompat.Builder builder
= new NotificationCompat
.Builder(getApplicationContext(),
channel_id)
.setSmallIcon(R.drawable.favicon)
.setAutoCancel(true)
.setVibrate(new long[]{1000, 1000, 1000, 1000, 1000})
.setOnlyAlertOnce(true)
.setContentIntent(notifyIntent);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
builder = builder.setContent(getCustomDesign(title, message));
}
else {
builder = builder.setContentTitle(title)
.setContentText(message)
.setSmallIcon(R.drawable.favicon);
}
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Check if the Android Version is greater than Oreo
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel(channel_id, "web_app",
NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(
notificationChannel);
}
notificationManager.notify(0, builder.build());
}
}
data
负载。你需要仅使用data
负载,并在 FCM 的onMessageRecieved()
中也需要像答案中一样接收数据负载。例如:你需要在onMessageRecieved()
中编写以下内容,以便触发body = remoteMessage.getData().get("bodys"); // bodys 是属性名称
。 - Peter Haddad