我使用firebase来构建我的项目
它还将使用FCM(firebase云消息)
但是有一个问题。
当应用程序处于后台时,我无法处理FCM(创建我的自定义通知)。
The official site tutorial说
案例1:应用前景 - >覆盖" onMessageReceived()"创建自定义通知。
案例2:应用背景 - >系统将直接创建通知。我们不需要做任何事情。因为它没有触发" onMessageReceived()"在这种情况下。
但是,如果我在应用为后台时无法执行任何操作,则无法创建自定义通知。 (例如,在用户单击通知后,它将弹出一个窗口以显示详细信息。)
那么当app在后台时,如何使用FCM处理通知?
答案 0 :(得分:7)
有一则坏消息。
Google更改版本'com.google.firebase:firebase-messaging:11.6.0'中的Firebase源代码。
handelIntent现在是“公共最终无效方法”。这意味着我们无法覆盖它
如果您要使用该解决方案,请将版本更改为“com.google.firebase:firebase-messaging:11.4.2”
试试我的方式。它可以完美地在项目构建版本上面安装Android 6.0(api level 23),我已经尝试过了。
有比official site tutorial更好的方法
官方网站表示,当应用处于后台时,系统会创建通知。因此,您无法通过覆盖“onMessageReceived()”来处理它。因为“onMessageReceived()”仅在app处于前台时触发。
但事实并非如此。实际上,通知(当应用程序处于后台时)由Firebase库创建。
我跟踪了firebase库代码。我找到了更好的方法。
步骤1.在FirebaseMessagingService中覆盖“handleIntent()”而不是“onMessageReceived()”
为什么:
因为该方法将触发app在前台或后台。因此,我们可以处理FCM消息并在两种情况下创建自定义通知。
@Override
public void handleIntent(Intent intent) {
Log.d( "FCM", "handleIntent ");
}
步骤2.解析来自FCM的消息
如何:
如果您不知道所设置消息的格式。打印并尝试解析它 Here is the basic illustration
Bundle bundle = intent.getExtras();
if (bundle != null) {
for (String key : bundle.keySet()) {
Object value = bundle.get(key);
Log.d("FCM", "Key: " + key + " Value: " + value);
}
}
步骤2.当应用程序处于后台时删除Firebase库创建的通知
为什么:
我们可以创建自定义通知。但Firebase库创建的通知仍然存在(实际上它是由“”super.handleIntent(intent)“”创建的。下面有详细说明。)。然后我们将有两个通知。这很奇怪。因此,我们必须删除Firebase库创建的通知
如何(项目构建级别是上面的Android 6.0):
识别我们要删除的通知并获取信息。并使用“notificationManager.cancel()”删除它们。
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);
}
}
}
答案 1 :(得分:2)
但是,如果我在应用程序是后台时无法执行任何操作,则无法创建自定义通知。 (例如,在用户单击通知后,它将弹出一个窗口以显示详细信息。)
那么当app在后台时,如何使用FCM处理通知?
首先,您需要创建发送到fcm服务器的正确消息有效内容。例如:
{
"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");
}
如果app未运行或处于后台,则以上所有内容均有效。如果您的应用是前台,则不会创建任何通知。相反,您将收到onMessageReceived()
事件,以便您可以在那里处理相同的数据(我猜您知道如何)。
参考:
https://firebase.google.com/docs/cloud-messaging/http-server-ref https://github.com/firebase/quickstart-android/tree/master/messaging
答案 2 :(得分:1)
您需要使用FCM数据消息来在android应用中创建自定义通知。即使您的应用处于后台,也会调用onMessageReceived
,以便您处理数据并显示自定义通知。< / p>
https://firebase.google.com/docs/cloud-messaging/android/receive
必须从服务器发送的数据消息格式:
{"message":{
"token":"Your Device Token",
"data":{
"Nick" : "Mario",
"body" : "great match!",
"Room" : "PortugalVSDenmark"
}
}
}
答案 3 :(得分:0)
FCM 如果您的应用已被杀,则不会发送背景通知,正如您在answer中所述的有关handleIntent()
解决方案的说法可能会有效对于某些设备和某些旧版本的 FCM ,如果您使用@override
方法在官方文档的firebase中没有描述,那么您可能会遇到一些问题在这里,你在own risk上使用它!。
解决方案是什么?
您需要在 FCM 旁边使用自己的推送通知服务,例如电报。
或在 GCM 旁边使用 SyncAdapter ,例如 Gmail 。
因此,如果你需要它像这些应用程序一样成功运作,你必须使用你自己的黑客。